require 'pathname' module Sass::Source class Map # A mapping from one source range to another. Indicates that `input` was # compiled to `output`. # # @!attribute input # @return [Sass::Source::Range] The source range in the input document. # # @!attribute output # @return [Sass::Source::Range] The source range in the output document. class Mapping < Struct.new(:input, :output) # @return [String] A string representation of the mapping. def inspect "#{input.inspect} => #{output.inspect}" end end # The mapping data ordered by the location in the target. # # @return [Array] attr_reader :data def initialize @data = [] end # Adds a new mapping from one source range to another. Multiple invocations # of this method should have each `output` range come after all previous ranges. # # @param input [Sass::Source::Range] # The source range in the input document. # @param output [Sass::Source::Range] # The source range in the output document. def add(input, output) @data.push(Mapping.new(input, output)) end # Shifts all output source ranges forward one or more lines. # # @param delta [Fixnum] The number of lines to shift the ranges forward. def shift_output_lines(delta) return if delta == 0 @data.each do |m| m.output.start_pos.line += delta m.output.end_pos.line += delta end end # Shifts any output source ranges that lie on the first line forward one or # more characters on that line. # # @param delta [Fixnum] The number of characters to shift the ranges # forward. def shift_output_offsets(delta) return if delta == 0 @data.each do |m| break if m.output.start_pos.line > 1 m.output.start_pos.offset += delta m.output.end_pos.offset += delta if m.output.end_pos.line > 1 end end # Returns the standard JSON representation of the source map. # # If the `:css_uri` option isn't specified, the `:css_path` and # `:sourcemap_path` options must both be specified. Any options may also be # specified alongside the `:css_uri` option. If `:css_uri` isn't specified, # it will be inferred from `:css_path` and `:sourcemap_path` using the # assumption that the local file system has the same layout as the server. # # If any source stylesheets use the default filesystem importer, sourcemap # generation will fail unless the ``:sourcemap_path` option is specified. # The layout of the local file system is assumed to be the same as the # layout of the server for the purposes of linking to source stylesheets # that use the filesystem importer. # # Regardless of which options are passed to this method, sourcemap # generation will fail if the source stylesheet contains any import that # uses a non-default importer that doesn't implement # \{Sass::Importers::Base#public\_url\}. # # @option options :css_uri [String] # The publicly-visible URI of the CSS output file. # @option options :css_path [String] # The local path of the CSS output file. # @option options :sourcemap_path [String] # The (eventual) local path of the sourcemap file. # @return [String] The JSON string. # @raise [ArgumentError] If neither `:css_uri` nor `:css_path` are # specified. # @raise [Sass::SyntaxError] If the public URL of a stylesheet cannot be # determined. def to_json(options) css_uri, css_path, sourcemap_path = [:css_uri, :css_path, :sourcemap_path].map {|o| options[o]} unless css_uri || (css_path && sourcemap_path) raise ArgumentError.new("Sass::Source::Map#to_json requires either " + "the :css_uri option or both the :css_path and :soucemap_path options.") end css_path &&= Pathname.pwd.join(Pathname.new(css_path)).cleanpath sourcemap_path &&= Pathname.pwd.join(Pathname.new(sourcemap_path)).cleanpath css_uri ||= css_path.relative_path_from(sourcemap_path.dirname).to_s result = "{\n" write_json_field(result, "version", "3", true) source_uri_to_id = {} id_to_source_uri = {} next_source_id = 0 line_data = [] segment_data_for_line = [] # These track data necessary for the delta coding. previous_target_line = nil previous_target_offset = 1 previous_source_line = 1 previous_source_offset = 1 previous_source_id = 0 @data.each do |m| file, importer = m.input.file, m.input.importer unless source_uri = importer && importer.public_url(file) if importer.is_a?(Sass::Importers::Filesystem) && sourcemap_path file_path = Pathname.new(importer.root).join(file) source_uri = file_path.relative_path_from(sourcemap_path.dirname).to_s else raise Sass::SyntaxError.new(<