pax_global_header00006660000000000000000000000064117232764130014520gustar00rootroot0000000000000052 comment=1632a5ee57c603a954b038aa9fa6da23bda06a3f ruby-source_map-master/000077500000000000000000000000001172327641300154715ustar00rootroot00000000000000ruby-source_map-master/.rspec000066400000000000000000000000261172327641300166040ustar00rootroot00000000000000--require spec_helper ruby-source_map-master/Gemfile000066400000000000000000000000451172327641300167630ustar00rootroot00000000000000source 'http://rubygems.org' gemspec ruby-source_map-master/Gemfile.lock000066400000000000000000000006661172327641300177230ustar00rootroot00000000000000PATH remote: . specs: source_map (3.0.1) json GEM remote: http://rubygems.org/ specs: diff-lcs (1.1.3) json (1.6.5) rake (0.9.2.2) rspec (2.8.0) rspec-core (~> 2.8.0) rspec-expectations (~> 2.8.0) rspec-mocks (~> 2.8.0) rspec-core (2.8.0) rspec-expectations (2.8.0) diff-lcs (~> 1.1.2) rspec-mocks (2.8.0) PLATFORMS ruby DEPENDENCIES rake rspec source_map! ruby-source_map-master/LICENSE000066400000000000000000000054601172327641300165030ustar00rootroot00000000000000Copyright (c) 2012 Conrad Irwin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- Portions of this file (in particular the VLQ decoding algorithms) are based on work from https://github.com/mozilla/source-map, which bears the following copyright notice: Copyright (c) 2009-2011, Mozilla Foundation and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the names of the Mozilla Foundation nor the names of project contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ruby-source_map-master/README.md000066400000000000000000000114641172327641300167560ustar00rootroot00000000000000The `source_map` gem provides an API for parsing, and an API for generating source maps in ruby. Source maps? ============ Source maps are Javascripts equivalent of the C `#line` functionality. They allow you to combine multiple javascript files into one, or minify your javascript yet still debug it as though you had done neither of these things. To do this you attach a SourceMap to a given generated javascript file, which contains a list of mappings between points in the generated file and points in the original files. This gem helps you create or parse those mapping files according to the SourceMaps version 3 spec. Installing ========== gem install source_map Generating a source map ======================= Let's say you have a directory full of javascript files, but you'd prefer them to be lumped together to avoid latency. ```ruby require 'source_map' file = File.open("public/combined.js", "w") map = SourceMap.new(:generated_output => file, :file => "combined.js", :source_root => "http://localhost:3000/") Dir["js/*"].each do |filename| map.add_generated File.read(filename), :source => filename.sub('public/', '') end map.save("public/combined.js.map") ``` This snippet will create two files for you. `combined.js` which contains all your javascripts lumped together, and `combined.js.map` which explains which bits of the file came from where. (Using the :generated_output feature to automatically write the combined.js file is totally optional if you don't need that feature). If you want more flexibility, there's an alternative API that requires you to do a bit more manual work: ```ruby require 'source_map' map = SourceMap.new(:file => 'combined.js', :source_root => 'http://localhost:3000/') my_crazy_process.each_fragment do |x| map.add_mapping( :generated_line => x.generated_line, :generated_col => 0, :source_line => x.source_line, :source_col => 0 :source => "foo.js" ) end ``` If you use this API, you'll probably need to read the Spec. Using a source map ================== You'll need Chrome version 19 or greater. Go to the developer console, and click on the settings cog; and then click "Enable source maps". Now, ensure that when you load `combined.js`, you also need to send an extra HTTP header: `X-SourceMap: /combined.js.map`. Finally ensure that eah of the source files can be reached by appending the value you provided to `:source`, to the value you provided for `:source_root`. NOTE: in theory you can (instead of using the `X-SourceMap` header) add a comment to the end of your generated file (`combined.js`) which looks like: //@ sourceMappingURL=/combined.js.map however I haven't had much luck with this. NOTE2: In theory you could use the Closure Inspector Firefox extension instead of Chrome 19, but I couldn't get it to work either (even when I tried in Firefox 3.6 which is the most recent version it supports). Sorry this is a bit rubbish :(. Future work =========== * An API to look up the position in the original source from a given position in the generated file. * I'd like to write a tool that given two source maps, composes them. Once that is done, then we could pipe `combined.js` through a minifier which generates a `combined.js.min` and a `combined.js.min.map`. And then we could combine `combined.js.map` and `combined.js.min.map` so that we can use our concatenated and minified code with the debugger with impunity. (The only such minifier that exists at the moment is the closure compier, maybe that will change...) * Supporting the index-file mode of SourceMaps (an alternative to the previous suggestion in some circumstances) Meta-Fu ======= This stuff is all available under the MIT license, bug-reports and feature suggestions welcome. Further Reading =============== This stuff is quite new so there's not exactly a lot of information about it: * the Version 3 Spec. * A javascript implementation (which helped this one) * Announcement of feature being released into Chrome. * The closure inspector was the first tool to allow reading of source maps, now seems a bit broken * Implementation status at Mozilla ruby-source_map-master/lib/000077500000000000000000000000001172327641300162375ustar00rootroot00000000000000ruby-source_map-master/lib/source_map.rb000066400000000000000000000036411172327641300207250ustar00rootroot00000000000000require 'rubygems' require 'json' require File.expand_path("../source_map/vlq.rb", __FILE__) require File.expand_path("../source_map/generator.rb", __FILE__) require File.expand_path("../source_map/parser.rb", __FILE__) class SourceMap include SourceMap::Generator include SourceMap::Parser # Create a new blank SourceMap # # Options may include: # # :file => String # See {#file} # :source_root => String # See {#source_root} # :generated_output => IO # See {#generated_output} # # :sources => Array[String] # See {#sources} # :names => Array[String] # See {#names} # # :version => 3 # Which version of SourceMap to use (only 3 is allowed) # def initialize(opts={}) unless (remain = opts.keys - [:generated_output, :file, :source_root, :sources, :names, :version]).empty? raise ArgumentError, "Unsupported options to SourceMap.new: #{remain.inspect}" end self.generated_output = opts[:generated_output] self.file = opts[:file] || '' self.source_root = opts[:source_root] || '' self.version = opts[:version] || 3 self.sources = opts[:sources] || [] self.names = opts[:names] || [] self.mappings = [] raise "version #{opts[:version]} not supported" if version != 3 end # The name of the file containing the code that this SourceMap describes. # (default "") attr_accessor :file # The URL/directory that contains the original source files. # # This is prefixed to the entries in ['sources'] # (default "") attr_accessor :source_root # The version of the SourceMap spec we're using. # (default 3) attr_accessor :version # The list of sources (used during parsing/generating) # These are relative to the source_root. # (default []) attr_accessor :sources # A list of names (used during parsing/generating) # (default []) attr_accessor :names # A list of mapping objects. attr_accessor :mappings end ruby-source_map-master/lib/source_map/000077500000000000000000000000001172327641300203745ustar00rootroot00000000000000ruby-source_map-master/lib/source_map/generator.rb000066400000000000000000000226671172327641300227240ustar00rootroot00000000000000class SourceMap module Generator # An object (responding to <<) that will be written to whenever # {add_generated} is called. # # @example # # File.open("/var/www/a.js.min"){ |f| # map = SourceMap.new(:generated_output => f) # map.add_generated('function(a,b,c){minified=1}\n', :source => 'a.js') # map.save('/var/www/a.js.map') # } # File.read('/var/www/a.js.min') == 'function(a,b,c){minified=1}\n' # attr_accessor :generated_output # Add the mapping for generated code to this source map. # # The first parameter is the generated text that you're going to add to the output, if # it contains multiple lines of code then it will be added to the source map as # several mappings. # # If present, the second parameter represents the original source of the generated # fragment, and may contain: # # :source => String, # The filename of the source fille that contains this fragment. # :source_line => Integer, # The line in that file that contains this fragment # :source_col => Integer, # The column in that line at which this fragment starts # :name => String # The original name for this variable. # :exact_position => Bool # Whether all lines in the generated fragment came from # the same position in the source. # # The :source key is required to set :source_line, :source_col or :name. # # If unset :source_line and :source_col default to 1,0 for the first line of the # generated fragment. # # Normally :source_line is incremented and :source_col reset at every line break in # the generated code (because we assume that you're copying a verbatim fragment from # the source into the generated code). If that is not the case, you can set # :exact_position => true, and then all lines in the generated output will be given # the same :source_line and :source_col. # # The :name property is used if the fragment you are adding contains only a name that # you have renamed in the source transformation. # # If you'd like to ensure that the source map stays in sync with the generated # source, consider calling {source_map.generated_output = StringIO.new} and then # accessing your generated javascript with {source_map.generated_output.string}, # otherwise be careful to always write to both. # # NOTE: By long-standing convention, the first line of a file is numbered 1, not 0. # # NOTE: when generating a source map, you should either use this method always, or use # the {#add_mapping} method always. # def add_generated(text, opts={}) if !opts[:source] && (opts[:name] || opts[:source_line] || opts[:source_col]) raise "mapping must have :source to have :source_line, :source_col or :name" elsif opts[:source_line] && opts[:source_line] < 1 raise "files start on line 1 (got :source_line => #{opts[:source_line]})" elsif !(remain = opts.keys - [:source, :source_line, :source_col, :name, :exact_position]).empty? raise "mapping had unexpected keys: #{remain.inspect}" end source_line = opts[:source_line] || 1 source_col = opts[:source_col] || 0 self.generated_line ||= 1 self.generated_col ||= 0 text.split(/(\n)/).each do |line| if line == "\n" self.generated_line += 1 self.generated_col = 0 unless opts[:exact_position] source_line += 1 source_col = 0 end elsif line != "" mapping = { :generated_line => generated_line, :generated_col => generated_col, } if opts[:source] mapping[:source] = opts[:source] mapping[:source_line] = source_line mapping[:source_col] = source_col mapping[:name] = opts[:name] if opts[:name] end mappings << mapping self.generated_col += line.size source_col += line.size unless opts[:exact_position] end end generated_output << text if generated_output end # Add a mapping to the list for this object. # # A mapping identifies a fragment of code that has been moved around during # transformation from the source file to the generated file. The fragment should # be contiguous and not contain any line breaks. # # Mappings are Hashes with a valid subset of the following 6 keys: # # :generated_line => Integer, # The line in the generated file that contains this fragment. # :generated_col => Integer, # The column in the generated_line that this mapping starts on # :source => String, # The filename of the source fille that contains this fragment. # :source_line => Integer, # The line in that file that contains this fragment. # :source_col => Integer, # The column in that line at which this fragment starts. # :name => String # The original name for this variable (if applicable). # # # The only 3 valid subsets of keys are: # [:generated_line, :generated_col] To indicate that this is a fragment in the # output file that you don't have the source for. # # [:generated_line, :generated_col, :source, :source_line, :source_col] To indicate # that this is a fragment in the output file that you do have the source for. # # [:generated_line, :generated_col, :source, :source_line, :source_col, :name] To # indicate that this is a particular identifier at a particular location in the original. # # Any other combination of keys would produce an invalid source map. # # NOTE: By long-standing convention, the first line of a file is numbered 1, not 0. # # NOTE: when generating a source map, you should either use this method always, # or use the {#add_generated} method always. # def add_mapping(map) if !map[:generated_line] || !map[:generated_col] raise "mapping must have :generated_line and :generated_col" elsif map[:source] && !(map[:source_line] && map[:source_col]) raise "mapping must have :source_line and :source_col if it has :source" elsif !map[:source] && (map[:source_line] || map[:source_col]) raise "mapping may not have a :source_line or :source_col without a :source" elsif map[:name] && !map[:source] raise "mapping may not have a :name without a :source" elsif map[:source_line] && map[:source_line] < 1 raise "files start on line 1 (got :source_line => #{map[:source_line]})" elsif map[:generated_line] < 1 raise "files start on line 1 (got :generated_line => #{map[:generated_line]})" elsif !(remain = map.keys - [:generated_line, :generated_col, :source, :source_line, :source_col, :name]).empty? raise "mapping had unexpected keys: #{remain.inspect}" end mappings << map end # Convert the map into an object suitable for direct serialisation. def as_json serialized_mappings = serialize_mappings! { 'version' => version, 'file' => file, 'sourceRoot' => source_root, 'sources' => sources, 'names' => names, 'mappings' => serialized_mappings } end # Convert the map to a string. def to_s as_json.to_json end # Write this map to a file. def save(file) File.open(file, "w"){ |f| f << to_s } end protected attr_reader :source_ids, :name_ids attr_accessor :generated_line, :generated_col # Get the id for the given file. If we've not # seen this file before, add it to the list. def source_id(file) source_ids[file] ||= ( sources << file sources.size - 1 ) end # Get the id for the given name. If we've not # seen this name before, add it to the list. def name_id(name) name_ids[name] ||= ( names << name names.size - 1 ) end # Encode a vlq. As each field in the output should be relative to the # previous occurance of that field, we keep track of each one. def vlq(num, type) ret = num - @previous_vlq[type] @previous_vlq[type] = num VLQ.encode(ret) end # Serialize the list of mappings into the string of base64 variable length # quanities. As a side-effect, regenerate the sources and names arrays. def serialize_mappings! # clear all internals as we're about to re-generate them. @sources = [] @source_ids = {} @names = [] @name_ids = {} @previous_vlq = Hash.new{ 0 } return "" if mappings.empty? by_lines = mappings.group_by{ |x| x[:generated_line] } (1..by_lines.keys.max).map do |line| # reset the generated_col on each line as indicated by the VLQ spec. # (the other values continue to be relative) @previous_vlq[:generated_col] = 0 fragments = (by_lines[line] || []).sort_by{ |x| x[:generated_col] } fragments.map do |map| serialize_mapping(map) end.join(",") end.join(";") end def serialize_mapping(map) item = vlq(map[:generated_col], :generated_col) if map[:source] item << vlq(source_id(map[:source]), :source) item << vlq(map[:source_line] - 1, :source_line) item << vlq(map[:source_col], :source_col) item << vlq(name_id(map[:name]), :name) if map[:name] end item end end end ruby-source_map-master/lib/source_map/parser.rb000066400000000000000000000066021172327641300222210ustar00rootroot00000000000000class SourceMap class ParserError < RuntimeError; end # Load a SourceMap from a Hash such as might be returned by # {SourceMap#as_json}. # def self.from_json(json) raise ParserError, "Cannot parse version: #{json['version']} of SourceMap" unless json['version'] == 3 map = new(:file => json['file'], :source_root => json['sourceRoot'], :sources => json['sources'], :names => json['names']) map.parse_mappings(json['mappings'] || '') map end # Load a SourceMap from a String. def self.from_s(str) from_json JSON.parse(str) end # Load a SourceMap from a file. def self.load(filename) from_s File.read(filename) end module Parser # Parse the mapping string from a SourceMap. # # The mappings string contains one comma-separated list of segments per line # in the output file, these lists are joined by semi-colons. # def parse_mappings(string) @previous = Hash.new{ 0 } string.split(";").each_with_index do |line, line_idx| # The generated_col resets to 0 at the start of every line, though # all the other differences are maintained. @previous[:generated_col] = 0 line.split(",").each do |segment| mappings << parse_mapping(segment, line_idx + 1) end end self.mappings = self.mappings.sort_by{ |x| [x[:generated_line], x[:generated_col]] } end # All the numbers in SourceMaps are stored as differences from each other, # so we need to remove the difference every time we read a number. def undiff(int, type) @previous[type] += int end # Parse an individual mapping. # # This is a list of variable-length-quanitity, with 1, 4 or 5 items. See the spec # https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit # for more details. def parse_mapping(segment, line_num) item = VLQ.decode_array(segment) unless [1, 4, 5].include?(item.size) raise ParserError, "In map for #{file}:#{line_num}: unparseable item: #{segment}" end map = { :generated_line => line_num, :generated_col => undiff(item[0], :generated_col), } if item.size >= 4 map[:source] = sources[undiff(item[1], :source_id)] map[:source_line] = undiff(item[2], :source_line) + 1 # line numbers are stored starting from 0 map[:source_col] = undiff(item[3], :source_col) map[:name] = names[undiff(item[4], :name_id)] if item[4] end if map[:generated_col] < 0 raise ParserError, "In map for #{file}:#{line_num}: unexpected generated_col: #{map[:generated_col]}" elsif map.key?(:source) && (map[:source].nil? || @previous[:source_id] < 0) raise ParserError, "In map for #{file}:#{line_num}: unknown source id: #{@previous[:source_id]}" elsif map.key?(:source_line) && map[:source_line] < 1 raise ParserError, "In map for #{file}:#{line_num}: unexpected source_line: #{map[:source_line]}" elsif map.key?(:source_col) && map[:source_col] < 0 raise ParserError, "In map for #{file}:#{line_num}: unexpected source_col: #{map[:source_col]}" elsif map.key?(:name) && (map[:name].nil? || @previous[:name_id] < 0) raise ParserError, "In map for #{file}:#{line_num}: unknown name id: #{@previous[:name_id]}" else map end end end end ruby-source_map-master/lib/source_map/vlq.rb000066400000000000000000000065671172327641300215410ustar00rootroot00000000000000class SourceMap # Support for encoding/decoding the variable length quantity format # described in the spec at: # # https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit # # This implementation is heavily based on https://github.com/mozilla/source-map # Copyright 2009-2011, Mozilla Foundation and contributors, BSD # module VLQ # A single base 64 digit can contain 6 bits of data. For the base 64 variable # length quantities we use in the source map spec, the first bit is the sign, # the next four bits are the actual value, and the 6th bit is the # continuation bit. The continuation bit tells us whether there are more # digits in this value following this digit. # # Continuation # | Sign # | | # V V # 101011 VLQ_BASE_SHIFT = 5; # binary: 100000 VLQ_BASE = 1 << VLQ_BASE_SHIFT; # binary: 011111 VLQ_BASE_MASK = VLQ_BASE - 1; # binary: 100000 VLQ_CONTINUATION_BIT = VLQ_BASE; BASE64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('') BASE64_VALUES = (0..64).inject({}){ |h, i| h.update BASE64_DIGITS[i] => i } # Returns the base 64 VLQ encoded value. def self.encode(int) vlq = to_vlq_signed(int) encoded = "" begin digit = vlq & VLQ_BASE_MASK vlq >>= VLQ_BASE_SHIFT digit |= VLQ_CONTINUATION_BIT if vlq > 0 encoded << base64_encode(digit) end while vlq > 0 encoded end # Decodes the next base 64 VLQ value from the given string and returns the # value and the rest of the string. def self.decode(str) vlq = 0 shift = 0 continue = true chars = str.split('') while continue char = chars.shift or raise "Expected more digits in base 64 VLQ value." digit = base64_decode(char) continue = false if (digit & VLQ_CONTINUATION_BIT) == 0 digit &= VLQ_BASE_MASK vlq += digit << shift shift += VLQ_BASE_SHIFT end [from_vlq_signed(vlq), chars.join('')] end # Decode an array of variable length quantities from the given string and # return them. def self.decode_array(str) output = [] while str != '' int, str = decode(str) output << int end output end protected def self.base64_encode(int) BASE64_DIGITS[int] or raise ArgumentError, "#{int} is not a valid base64 digit" end def self.base64_decode(char) BASE64_VALUES[char] or raise ArgumentError, "#{char} is not a valid base64 digit" end # Converts from a two's-complement integer to an integer where the # sign bit is placed in the least significant bit. For example, as decimals: # 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) # 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) def self.to_vlq_signed(int) if int < 0 ((-int) << 1) + 1 else int << 1 end end # Converts to a two's-complement value from a value where the sign bit is # placed in the least significant bit. For example, as decimals: # # 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 # 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 def self.from_vlq_signed(vlq) if vlq & 1 == 1 -(vlq >> 1) else vlq >> 1 end end end end ruby-source_map-master/source_map.gemspec000066400000000000000000000016151172327641300211760ustar00rootroot00000000000000Gem::Specification.new do |gem| gem.name = 'source_map' gem.version = '3.0.1' gem.summary = 'Ruby support for source_maps (version 3)' gem.description = <<-DESC Ruby support for Source Maps allows you to interact with Source Maps in Ruby. This lets you do things like concatenate different javascript files and still debug them as though they were separate files. See the spec for more information: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit DESC gem.authors = ['Conrad Irwin'] gem.email = %w(conrad.irwin@gmail.com) gem.homepage = 'http://github.com/ConradIrwin/ruby-source_map' gem.license = 'MIT' gem.required_ruby_version = '>= 1.8.7' gem.add_dependency 'json' gem.add_development_dependency 'rake' gem.add_development_dependency 'rspec' gem.files = Dir[*%w( lib/* lib/*/* LICENSE* README*)] end ruby-source_map-master/spec/000077500000000000000000000000001172327641300164235ustar00rootroot00000000000000ruby-source_map-master/spec/generator_spec.rb000066400000000000000000000324411172327641300217540ustar00rootroot00000000000000describe SourceMap::Generator do before :all do class SourceMap public :mappings, :generated_line, :generated_col, :serialize_mapping end end describe '#add_mapping' do it 'should allow a mapping with no source data' do a = {:generated_line => 2, :generated_col => 1} SourceMap.new.tap{ |x| x.add_mapping(a) }.mappings.should == [a] end it 'should allow a mapping with source data' do a = {:generated_line => 2, :generated_col => 1, :source => "a.js", :source_line => 1, :source_col => 0} SourceMap.new.tap{ |x| x.add_mapping(a) }.mappings.should == [a] end it 'should allow a mapping with name and source data' do a = {:generated_line => 2, :generated_col => 1, :source => "a.js", :source_line => 1, :source_col => 0, :name => 'moo'} SourceMap.new.tap{ |x| x.add_mapping(a) }.mappings.should == [a] end it 'should disallow a mapping with no generated data' do a = {:source => "a.js", :source_line => 1, :source_col => 0} lambda{ SourceMap.new.add_mapping(a) }.should raise_error(/generated_line/) end it 'should explain about invalid keys' do a = {:generated_line => 2, :generated_col => 1, :souce => 3} lambda{ SourceMap.new.add_mapping(a) }.should raise_error(/souce/) end end describe '#add_generated' do it 'should work with no source information' do SourceMap.new.tap{ |x| x.add_generated('foo') }.mappings.should == [{ :generated_line => 1, :generated_col => 0, }] end it 'should work with some source information' do SourceMap.new.tap{ |x| x.add_generated('foo', :source => 'a.js') }.mappings.should == [{ :generated_line => 1, :generated_col => 0, :source => 'a.js', :source_line => 1, :source_col => 0 }] end it 'should work with all source information' do SourceMap.new.tap{ |x| x.add_generated('foo', :source => 'a.js', :source_line => 2, :source_col => 2) }.mappings.should == [{ :generated_line => 1, :generated_col => 0, :source => 'a.js', :source_line => 2, :source_col => 2 }] end it 'should work with name information' do SourceMap.new.tap{ |x| x.add_generated('foo', :source => 'a.js', :name => 'fred') }.mappings.should == [{ :generated_line => 1, :generated_col => 0, :source => 'a.js', :source_line => 1, :source_col => 0, :name => 'fred' }] end it 'should increment generated_col' do SourceMap.new.tap{ |x| x.add_generated('foo', :source => 'a.js', :name => 'fred') }.generated_col.should == 3 end it 'should increated generated_line' do SourceMap.new.tap{ |x| x.add_generated("\nf\no\no", :source => 'a.js', :name => 'fred') }.generated_line.should == 4 end it 'should start from the previous generated_col' do SourceMap.new.tap{ |x| x.add_generated('foo') x.add_generated("\nbar") x.add_generated('baz') }.mappings.should == [ {:generated_line => 1, :generated_col => 0}, {:generated_line => 2, :generated_col => 0}, {:generated_line => 2, :generated_col => 3}, ] end it 'should split multline fragments' do SourceMap.new.tap{ |x| x.add_generated("foo\nbarbaz\n") }.mappings.should == [ {:generated_line => 1, :generated_col => 0}, {:generated_line => 2, :generated_col => 0}, ] end it 'should keep the source_line and source_col in sync with multiline fragments' do SourceMap.new.tap{ |x| x.add_generated("foo\nbarbaz\n", :source => 'a.js', :source_line => 10, :source_col => 6) }.mappings.should == [ {:generated_line => 1, :generated_col => 0, :source => 'a.js', :source_line => 10, :source_col => 6}, {:generated_line => 2, :generated_col => 0, :source => 'a.js', :source_line => 11, :source_col => 0}, ] end it 'should not move source_line and source_col if exact_position is given' do SourceMap.new.tap{ |x| x.add_generated("foo\nbarbaz\n", :source => 'a.js', :source_line => 10, :source_col => 6, :exact_position => true) }.mappings.should == [ {:generated_line => 1, :generated_col => 0, :source => 'a.js', :source_line => 10, :source_col => 6}, {:generated_line => 2, :generated_col => 0, :source => 'a.js', :source_line => 10, :source_col => 6}, ] end end describe '#generated_output' do it 'should be written to for each fragment' do map = SourceMap.new(:generated_output => (go = StringIO.new)) map.generated_output.should == go go.should_receive(:<<).with('bananas') map.add_generated('bananas') go.should_receive(:<<).with('elephants') map.add_generated('elephants') end it 'should be written to once for multiline fragments' do map = SourceMap.new map.generated_output = StringIO.new map.generated_output.should_receive(:<<).with("bananas\nelephants\n") map.add_generated("bananas\nelephants\n") end end describe 'as_json' do it 'should by valid by default' do SourceMap.new.as_json.should == { 'version' => 3, 'file' => '', 'sourceRoot' => '', 'sources' => [], 'names' => [], 'mappings' => '' } end it 'should preserve source_root from constructor' do SourceMap.new(:source_root => "http://localhost/debug/").as_json['sourceRoot'].should == "http://localhost/debug/" end it 'should preserve file from constructor' do SourceMap.new(:file => 'a.js').as_json['file'].should == "a.js" end it 'should include each name exactly once' do map = SourceMap.new map.add_generated("foo\n", :source => 'a.js', :name => 'baa') map.add_generated("foo\n", :source => 'a.js', :name => 'baa') map.add_generated("foo\n", :source => 'a.js', :name => 'aab') map.add_generated("foo\n", :source => 'a.js', :name => 'baa') map.as_json['names'].should == ['baa', 'aab'] end it 'should include each source exactly once' do map = SourceMap.new map.add_generated("foo\n", :source => 'a.js') map.add_generated("foo\n", :source => 'a.js') map.add_generated("foo\n", :source => 'b.js') map.add_generated("foo\n", :source => 'a.js') map.as_json['sources'].should == ['a.js', 'b.js'] end it 'should have a semi-colon between every line' do map = SourceMap.new map.add_mapping(:generated_line => 3, :generated_col =>0) map.add_mapping(:generated_line => 4, :generated_col =>0) map.add_mapping(:generated_line => 6, :generated_col =>0) map.as_json['mappings'].should == ';;A;A;;A' end it 'should have a comma between each fragment on the same line' do map = SourceMap.new map.add_mapping(:generated_line => 1, :generated_col =>0) map.add_mapping(:generated_line => 1, :generated_col =>1) map.add_mapping(:generated_line => 1, :generated_col =>2) map.as_json['mappings'].should == 'A,C,C' end it 'should reset the vlq offset for the column when starting a new line' do map = SourceMap.new map.add_mapping(:generated_line => 1, :generated_col =>0) map.add_mapping(:generated_line => 1, :generated_col =>1) map.add_mapping(:generated_line => 2, :generated_col =>2) map.as_json['mappings'].should == 'A,C;E' end it 'should encode source file positions relativesly' do map = SourceMap.new map.add_mapping(:generated_line => 1, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 1) map.add_mapping(:generated_line => 2, :generated_col =>0, :source => 'b.js', :source_col => 0, :source_line => 1) map.add_mapping(:generated_line => 3, :generated_col =>0, :source => 'b.js', :source_col => 0, :source_line => 1) map.add_mapping(:generated_line => 4, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 1) map.as_json['mappings'].should == 'AAAA;ACAA;AAAA;ADAA' end it 'should encode source_line relatively (even when switching sources)' do map = SourceMap.new map.add_mapping(:generated_line => 1, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 1) map.add_mapping(:generated_line => 2, :generated_col =>0, :source => 'b.js', :source_col => 0, :source_line => 1) map.add_mapping(:generated_line => 3, :generated_col =>0, :source => 'b.js', :source_col => 0, :source_line => 2) map.add_mapping(:generated_line => 4, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 4) map.as_json['mappings'].should == 'AAAA;ACAA;AACA;ADEA' end it 'should encode source_col relatively (ignoring changes to source and source_line)' do map = SourceMap.new map.add_mapping(:generated_line => 1, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 1) map.add_mapping(:generated_line => 2, :generated_col =>0, :source => 'b.js', :source_col => 5, :source_line => 1) map.add_mapping(:generated_line => 3, :generated_col =>0, :source => 'b.js', :source_col => 9, :source_line => 2) map.add_mapping(:generated_line => 4, :generated_col =>0, :source => 'a.js', :source_col => 2, :source_line => 4) map.as_json['mappings'].should == 'AAAA;ACAK;AACI;ADEP' end it 'should encode name positions relatively' do map = SourceMap.new map.add_mapping(:generated_line => 1, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 1, :name => 'a') map.add_mapping(:generated_line => 2, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 2, :name => 'b') map.add_mapping(:generated_line => 3, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 3, :name => 'b') map.add_mapping(:generated_line => 4, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 4, :name => 'a') map.as_json['mappings'].should == 'AAAAA;AACAC;AACAA;AACAD' end it 'should re-order mappings by line' do map = SourceMap.new # same mappings as 'should encode source_col relatively (ignoring changes to source and source_line)' map.add_mapping(:generated_line => 4, :generated_col =>0, :source => 'a.js', :source_col => 2, :source_line => 4) map.add_mapping(:generated_line => 1, :generated_col =>0, :source => 'a.js', :source_col => 0, :source_line => 1) map.add_mapping(:generated_line => 3, :generated_col =>0, :source => 'b.js', :source_col => 9, :source_line => 2) map.add_mapping(:generated_line => 2, :generated_col =>0, :source => 'b.js', :source_col => 5, :source_line => 1) map.as_json['mappings'].should == 'AAAA;ACAK;AACI;ADEP' end it 'should re-order fragments within a line if necessary' do map = SourceMap.new map.add_mapping(:generated_line => 1, :generated_col =>3) map.add_mapping(:generated_line => 1, :generated_col =>1) map.add_mapping(:generated_line => 1, :generated_col =>0) map.as_json['mappings'].should == 'A,C,E' end end it 'should pass the test from https://github.com/mozilla/source-map/blob/master/test/test-source-map-generator.js' do map = SourceMap.new(:file => 'min.js', :source_root => '/the/root') map.add_mapping(:generated_line => 1, :generated_col => 1, :source_line => 1, :source_col => 1, :source => 'one.js') map.add_mapping(:generated_line => 1, :generated_col => 5, :source_line => 1, :source_col => 5, :source => 'one.js') map.add_mapping(:generated_line => 1, :generated_col => 9, :source_line => 1, :source_col => 11, :source => 'one.js') map.add_mapping(:generated_line => 1, :generated_col => 18, :source_line => 1, :source_col => 21, :source => 'one.js', :name => 'bar') map.add_mapping(:generated_line => 1, :generated_col => 21, :source_line => 2, :source_col => 3, :source => 'one.js') map.add_mapping(:generated_line => 1, :generated_col => 28, :source_line => 2, :source_col => 10, :source => 'one.js', :name => 'baz') map.add_mapping(:generated_line => 1, :generated_col => 32, :source_line => 2, :source_col => 14, :source => 'one.js', :name => 'bar') map.add_mapping(:generated_line => 2, :generated_col => 1, :source_line => 1, :source_col => 1, :source => 'two.js') map.add_mapping(:generated_line => 2, :generated_col => 5, :source_line => 1, :source_col => 5, :source => 'two.js') map.add_mapping(:generated_line => 2, :generated_col => 9, :source_line => 1, :source_col => 11, :source => 'two.js') map.add_mapping(:generated_line => 2, :generated_col => 18, :source_line => 1, :source_col => 21, :source => 'two.js', :name => 'n') map.add_mapping(:generated_line => 2, :generated_col => 21, :source_line => 2, :source_col => 3, :source => 'two.js') map.add_mapping(:generated_line => 2, :generated_col => 28, :source_line => 2, :source_col => 10, :source => 'two.js', :name => 'n') map.as_json.should == { 'version' => 3, 'file' => 'min.js', 'names' => ['bar', 'baz', 'n'], 'sources' => ['one.js', 'two.js'], 'sourceRoot' => '/the/root', 'mappings' => 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA' } end end ruby-source_map-master/spec/parser_spec.rb000066400000000000000000000146141172327641300212640ustar00rootroot00000000000000describe SourceMap::Parser do describe '.from_json' do it "should copy across the file" do SourceMap.from_json('version' => 3, 'file' => 'foo.js').file.should == 'foo.js' end it "should copy across the sourceRoot" do SourceMap.from_json('version' => 3, 'sourceRoot' => 'http://example.com/').source_root.should == 'http://example.com/' end it "should copy across the sources" do SourceMap.from_json('version' => 3, 'sources' => ['a.js', 'b.js']).sources.should == ['a.js', 'b.js'] end it "should copy across the names" do SourceMap.from_json('version' => 3, 'names' => ['a', 'b', 'c']).names.should == ['a', 'b', 'c'] end end describe '#parse_mappings' do it 'should pass through to #parse_mapping with the correct line number' do map = SourceMap.new map.should_receive(:parse_mapping).with('A', 1).and_return({:generated_line => 1, :generated_col => 1}) map.should_receive(:parse_mapping).with('C', 1).and_return({:generated_line => 2, :generated_col => 1}) map.should_receive(:parse_mapping).with('E', 2).and_return({:generated_line => 3, :generated_col => 1}) map.should_receive(:parse_mapping).with('G', 4).and_return({:generated_line => 4, :generated_col => 1}) map.should_receive(:parse_mapping).with('H', 5).and_return({:generated_line => 5, :generated_col => 1}) map.parse_mappings('A,C;E;;G,;H') end end describe '#parse_mapping' do it 'should parse the generated_col' do map = SourceMap.new map.parse_mappings('E') map.mappings.should == [ {:generated_line => 1, :generated_col => 2} ] end it 'should append to map.mappings' do map = SourceMap.new map.parse_mappings('A;E') map.mappings.should == [ {:generated_line => 1, :generated_col => 0}, {:generated_line => 2, :generated_col => 2} ] end it 'should sort the segments on a line ascendingly' do map = SourceMap.new map.parse_mappings('E,F') map.mappings.should == [ {:generated_line => 1, :generated_col => 0}, {:generated_line => 1, :generated_col => 2} ] end it 'should parse the source name' do map = SourceMap.new(:sources => ['a.js', 'b.js']) map.parse_mappings('AAAA;ACAA') map.mappings.should == [ {:generated_line => 1, :generated_col => 0, :source => 'a.js', :source_line => 1, :source_col => 0}, {:generated_line => 2, :generated_col => 0, :source => 'b.js', :source_line => 1, :source_col => 0} ] end it 'should parse the source line' do map = SourceMap.new(:sources => ['a.js', 'b.js']) map.parse_mappings('AACA;AADA') map.mappings.should == [ {:generated_line => 1, :generated_col => 0, :source => 'a.js', :source_line => 2, :source_col => 0}, {:generated_line => 2, :generated_col => 0, :source => 'a.js', :source_line => 1, :source_col => 0} ] end it 'should parse the source cols' do map = SourceMap.new(:sources => ['a.js', 'b.js']) map.parse_mappings('AAAE;AAAS') map.mappings.should == [ {:generated_line => 1, :generated_col => 0, :source => 'a.js', :source_line => 1, :source_col => 2}, {:generated_line => 2, :generated_col => 0, :source => 'a.js', :source_line => 1, :source_col => 11} ] end it 'should parse the names' do map = SourceMap.new(:names => ['d', 'e'], :sources => ['a.js']) map.parse_mappings('AAAAC;AAAAD') map.mappings.should == [ {:generated_line => 1, :generated_col => 0, :source => 'a.js', :source_line => 1, :source_col => 0, :name => 'e'}, {:generated_line => 2, :generated_col => 0, :source => 'a.js', :source_line => 1, :source_col => 0, :name => 'd'} ] end it 'should raise an error on an unknown source' do map = SourceMap.new(:sources => ['a.js'], :file => 'moo.js') lambda{ map.parse_mappings(';AEAA') }.should raise_error(/In map for moo.js:2: unknown source id: 2/) end it 'should raise an error on negative sources' do map = SourceMap.new(:sources => ['a.js'], :file => 'moo.js') lambda{ map.parse_mappings('ADAA') }.should raise_error(/In map for moo.js:1: unknown source id: -1/) end it 'should raise an error on the zeroth source lines' do map = SourceMap.new(:sources => ['a.js'], :file => 'moo.js') lambda{ map.parse_mappings('AADA') }.should raise_error(/In map for moo.js:1: unexpected source_line: 0/) end end it 'should be able to parse the example from mozilla/source-map' do map = { :version => 3, :file => 'min.js', :names => ['bar', 'baz', 'n'], :sources => ['one.js', 'two.js'], :sourceRoot => '/the/root', :mappings => 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA' }.to_json SourceMap.from_s(map).mappings.should == [ {:generated_line => 1, :generated_col => 1, :source => 'one.js', :source_line => 1, :source_col => 1}, {:generated_line => 1, :generated_col => 5, :source => 'one.js', :source_line => 1, :source_col => 5}, {:generated_line => 1, :generated_col => 9, :source => 'one.js', :source_line => 1, :source_col => 11}, {:generated_line => 1, :generated_col => 18, :source => 'one.js', :source_line => 1, :source_col => 21, :name => 'bar'}, {:generated_line => 1, :generated_col => 21, :source => 'one.js', :source_line => 2, :source_col => 3}, {:generated_line => 1, :generated_col => 28, :source => 'one.js', :source_line => 2, :source_col => 10, :name => 'baz'}, {:generated_line => 1, :generated_col => 32, :source => 'one.js', :source_line => 2, :source_col => 14, :name => 'bar'}, {:generated_line => 2, :generated_col => 1, :source => 'two.js', :source_line => 1, :source_col => 1}, {:generated_line => 2, :generated_col => 5, :source => 'two.js', :source_line => 1, :source_col => 5}, {:generated_line => 2, :generated_col => 9, :source => 'two.js', :source_line => 1, :source_col => 11}, {:generated_line => 2, :generated_col => 18, :source => 'two.js', :source_line => 1, :source_col => 21, :name => 'n'}, {:generated_line => 2, :generated_col => 21, :source => 'two.js', :source_line => 2, :source_col => 3}, {:generated_line => 2, :generated_col => 28, :source => 'two.js', :source_line => 2, :source_col => 10, :name => 'n'}, ] end end ruby-source_map-master/spec/spec_helper.rb000066400000000000000000000000331172327641300212350ustar00rootroot00000000000000require './lib/source_map' ruby-source_map-master/spec/vlq_spec.rb000066400000000000000000000027251172327641300205720ustar00rootroot00000000000000 describe SourceMap::VLQ do before do def self.encode(x) SourceMap::VLQ.encode(x) end def self.decode(x) SourceMap::VLQ.decode(x)[0] end def self.decode_array(x) SourceMap::VLQ.decode_array(x) end end it 'should be able to encode 0' do encode(0).should == 'A' end it 'should be able to decode 0' do decode('A').should == 0 end it 'should be able to encode a positive integer' do encode(1).should == 'C' encode(2).should == 'E' encode(5).should == 'K' encode(1000).should == 'w+B' encode(100000).should == 'gqjG' end it 'should be able to decode a positive integer' do decode('C').should == 1 decode('E').should == 2 decode('K').should == 5 decode('w+B').should == 1000 decode('gqjG').should == 100000 end it 'should be able to encode a negative integer' do encode(-1).should == 'D' encode(-2).should == 'F' encode(-5).should == 'L' encode(-1000).should == 'x+B' encode(-100000).should == 'hqjG' end it 'should be able to decode a negative integer' do decode('D').should == -1 decode('F').should == -2 decode('L').should == -5 decode('x+B').should == -1000 decode('hqjG').should == -100000 end it 'should be able to decode an array' do decode_array('DFLx+BhqjG').should == [-1, -2, -5, -1000, -100000] decode_array('CEKw+BgqjG').should == [1, 2, 5, 1000, 100000] decode_array('/+Z').should == [-13295] end end