Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
**Master**

- Preserve input maps and nested index-map structure when concatenating source maps. [Oskar Eichler](https://github.com/OskarEichler)

Get upgrade notes from Sprockets 3.x to 4.x at https://github.com/rails/sprockets/blob/master/UPGRADING.md

# 4.4.1
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -714,3 +714,5 @@ Please see the [CHANGELOG](https://github.com/rails/sprockets/tree/master/CHANGE

## License
Sprockets is released under the [MIT License](MIT-LICENSE).

Source map concatenation preserves both inputs and supports nested index maps so repeated concatenation retains the same line offsets.
62 changes: 50 additions & 12 deletions lib/sprockets/source_map_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,26 +72,26 @@ def format_source_map(map, input)
# Returns a new source map hash.
def concat_source_maps(a, b)
return a || b unless a && b
a = make_index_map(a)
a = make_index_map(a).dup
a["sections"] = a["sections"].dup
b = make_index_map(b)

offset = 0
if a["sections"].count != 0 && !a["sections"].last["map"]["mappings"].empty?
last_line_count = a["sections"].last["map"].delete("x_sprockets_linecount")
offset += last_line_count || 1

last_offset = a["sections"].last["offset"]["line"]
offset += last_offset
a["sections"].reverse_each.with_index do |section, reverse_index|
map, line_count = extract_last_source_map_line(section["map"])
next unless line_count

section = section.dup
section["map"] = map
a["sections"][-reverse_index - 1] = section
offset = section["offset"]["line"] + line_count
break
end

a["sections"] += b["sections"].map do |section|
{
"offset" => section["offset"].merge({ "line" => section["offset"]["line"] + offset }),
"map" => section["map"].merge({
"sources" => section["map"]["sources"].map do |source|
PathUtils.relative_path_from(a["file"], PathUtils.join(File.dirname(b["file"]), source))
end
})
"map" => rebase_source_map_sources(section["map"], from: b["file"], to: a["file"])
}
end
a
Expand Down Expand Up @@ -479,5 +479,43 @@ def vlq_decode_mappings(str)

mappings
end

private

def extract_last_source_map_line(map)
if map["sections"]
map["sections"].reverse_each.with_index do |section, reverse_index|
child, line_count = extract_last_source_map_line(section["map"])
next unless line_count

section = section.dup
section["map"] = child
map = map.dup
map["sections"] = map["sections"].dup
map["sections"][-reverse_index - 1] = section
return map, section["offset"]["line"] + line_count
end
[map, nil]
elsif map.key?("x_sprockets_linecount") || !map["mappings"].empty?
map = map.dup
[map, map.delete("x_sprockets_linecount") || 1]
else
[map, nil]
end
end

def rebase_source_map_sources(map, from:, to:)
map = map.dup
if map["sections"]
map["sections"] = map["sections"].map do |section|
section.merge("map" => rebase_source_map_sources(section["map"], from: from, to: to))
end
else
map["sources"] = map["sources"].map do |source|
PathUtils.relative_path_from(to, PathUtils.join(File.dirname(from), source))
end
end
map
end
end
end