From 045d4a499238bcc1052cd3237de09b595b838e8b Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 2 Nov 2020 15:42:13 +0900 Subject: [PATCH 1/2] Embed sourcesContent in source maps --- .../preprocessors/default_source_map.rb | 4 +- lib/sprockets/source_map_processor.rb | 15 +- lib/sprockets/source_map_utils.rb | 4 +- test/test_asset.rb | 4 +- test/test_coffee_script_processor.rb | 3 +- test/test_source_maps.rb | 205 ++++++++++++++---- 6 files changed, 176 insertions(+), 59 deletions(-) diff --git a/lib/sprockets/preprocessors/default_source_map.rb b/lib/sprockets/preprocessors/default_source_map.rb index 7f8c9340e..45e223551 100644 --- a/lib/sprockets/preprocessors/default_source_map.rb +++ b/lib/sprockets/preprocessors/default_source_map.rb @@ -15,14 +15,12 @@ def call(input) load_path = input[:load_path] lines = input[:data].lines.length basename = File.basename(filename) - mime_exts = input[:environment].config[:mime_exts] - pipeline_exts = input[:environment].config[:pipeline_exts] if map.nil? || map.empty? result[:map] = { "version" => 3, "file" => PathUtils.split_subpath(load_path, filename), "mappings" => default_mappings(lines), - "sources" => [PathUtils.set_pipeline(basename, mime_exts, pipeline_exts, :source)], + "sources" => [basename], "names" => [] } else diff --git a/lib/sprockets/source_map_processor.rb b/lib/sprockets/source_map_processor.rb index 02da882d4..0ae841e20 100644 --- a/lib/sprockets/source_map_processor.rb +++ b/lib/sprockets/source_map_processor.rb @@ -38,12 +38,17 @@ def self.call(input) dependencies.merge(asset.metadata[:dependencies]) map["file"] = PathUtils.split_subpath(input[:load_path], input[:filename]) - sources = map["sections"] ? map["sections"].map { |s| s["map"]["sources"] }.flatten : map["sources"] + sections = map["sections"] ? map["sections"] : [{ "map" => map }] + sections.each do |section| + section["map"]["sourcesContent"] = section["map"]["sources"].map do |source| + source = PathUtils.join(File.dirname(map["file"]), source) + uri, _ = env.resolve!(source) + next nil unless uri.start_with? "file://" - sources.each do |source| - source = PathUtils.join(File.dirname(map["file"]), source) - uri, _ = env.resolve!(source) - links << uri + path = URIUtils.split_file_uri(uri)[2] + next nil unless File.exist?(path) + File.read(path) + end end json = JSON.generate(map) diff --git a/lib/sprockets/source_map_utils.rb b/lib/sprockets/source_map_utils.rb index b686bbb50..7d18fff31 100644 --- a/lib/sprockets/source_map_utils.rb +++ b/lib/sprockets/source_map_utils.rb @@ -38,8 +38,6 @@ def format_source_map(map, input) filename = input[:filename] load_path = input[:load_path] load_paths = input[:environment].config[:paths] - mime_exts = input[:environment].config[:mime_exts] - pipeline_exts = input[:environment].config[:pipeline_exts] file = PathUtils.split_subpath(load_path, filename) { "version" => 3, @@ -50,7 +48,7 @@ def format_source_map(map, input) source = PathUtils.join(File.dirname(filename), source) unless PathUtils.absolute_path?(source) _, source = PathUtils.paths_split(load_paths, source) source = PathUtils.relative_path_from(file, source) - PathUtils.set_pipeline(source, mime_exts, pipeline_exts, :source) + source end, "names" => map["names"] } diff --git a/test/test_asset.rb b/test/test_asset.rb index 6ca369da8..1d2c48ec6 100644 --- a/test/test_asset.rb +++ b/test/test_asset.rb @@ -1147,7 +1147,7 @@ def setup end test "digest path" do - assert_equal "application.debug-dee339ce0ee2dc7cdfa0d36dff3ef946cebe1bd3e414515d40c3cafc49c0a51a.js", + assert_equal "application.debug-5bafea519f7aae9679023c6441b8c3623b4147cf5bca607abc5aab0c35ce6618.js", @asset.digest_path end @@ -1180,7 +1180,7 @@ def setup $('search').focus(); }); -//# sourceMappingURL=application.js-95e519d4e0f0a5c4c7d24787ded990b0d027f7ad30b39f402c4c5e3196a41e8b.map +//# sourceMappingURL=application.js-ba55f2ffb2663c056b196f7874897ca13fc2fb892dfdda1f9535d105e3c9ee25.map EOS assert_equal expected, @asset.to_s diff --git a/test/test_coffee_script_processor.rb b/test/test_coffee_script_processor.rb index 86a5c0e9f..7c698b99e 100644 --- a/test/test_coffee_script_processor.rb +++ b/test/test_coffee_script_processor.rb @@ -25,7 +25,8 @@ def test_compile_coffee_script_template_to_js result = Sprockets::CoffeeScriptProcessor.call(input) assert result[:data].match(/var square/) assert_equal 13, Sprockets::SourceMapUtils.decode_source_map(result[:map])[:mappings].size - assert_equal ["squared.source.coffee"], result[:map]["sources"] + assert_equal ["squared.coffee"], result[:map]["sources"] + assert_nil result[:map]["sourcesContent"] end def test_cache_key diff --git a/test/test_source_maps.rb b/test/test_source_maps.rb index 255658031..f09de7b20 100644 --- a/test/test_source_maps.rb +++ b/test/test_source_maps.rb @@ -16,7 +16,10 @@ def setup end def get_sources(map) - map["sections"].reduce([]) { |r, s| r | s["map"]["sources"] } + map["sections"].reduce([]) { |r, s| r + s["map"]["sources"] } + end + def get_sourcesContent(map) + map["sections"].reduce([]) { |r, s| r + (s["map"]["sourcesContent"] || s["map"]["sources"].map { nil }) } end # Offset should be the line that the asset starts on minus one @@ -47,17 +50,19 @@ def get_sources(map) test "builds a source map for js files" do asset = @env['child.js'] map = asset.metadata[:map] - assert_equal ["child.source.js"], get_sources(map) + assert_equal ["child.js"], get_sources(map) + assert_equal [nil], get_sourcesContent(map) end test "builds a concatenated source map" do asset = @env['application.js'] map = asset.metadata[:map] assert_equal [ - "project.source.coffee", - "users.source.coffee", - "application.source.coffee" + "project.coffee", + "users.coffee", + "application.coffee" ], get_sources(map) + assert_equal [nil, nil, nil], get_sourcesContent(map) end test "builds a minified source map" do @@ -67,20 +72,22 @@ def get_sources(map) map = Sprockets::SourceMapUtils.decode_source_map(asset.metadata[:map]) assert map[:mappings].all? { |mapping| mapping[:generated][0] == 1 } assert_equal [ - "project.source.coffee", - "users.source.coffee", - "application.source.coffee" + "project.coffee", + "users.coffee", + "application.coffee" ], map[:sources] + assert_nil map[:sourcesContent] end test "builds a source map with js dependency" do asset = @env['parent.js'] map = asset.metadata[:map] assert_equal [ - "child.source.js", - "users.source.coffee", - "parent.source.js" + "child.js", + "users.coffee", + "parent.js" ], get_sources(map) + assert_equal [nil, nil, nil], get_sourcesContent(map) end test "rebuilds a source map when related dependency has changed" do @@ -111,9 +118,7 @@ def get_sources(map) assert_equal fixture_path('source-maps/coffee/main.coffee'), asset.filename assert_equal "coffee/main.js.map", asset.logical_path assert_equal "application/js-sourcemap+json", asset.content_type - assert_equal [ - "file://#{fixture_path_for_uri('source-maps/coffee/main.coffee')}?type=text/coffeescript&pipeline=source" - ], normalize_uris(asset.links) + assert_equal [], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ @@ -126,7 +131,37 @@ def get_sources(map) "version" => 3, "file" => "coffee/main.coffee", "mappings" => "AACA;AAAA,MAAA,sDAAA;IAAA;;EAAA,MAAA,GAAW;;EACX,QAAA,GAAW;;EAGX,IAAgB,QAAhB;IAAA,MAAA,GAAS,CAAC,GAAV;;;EAGA,MAAA,GAAS,SAAC,CAAD;WAAO,CAAA,GAAI;EAAX;;EAGT,IAAA,GAAO,CAAC,CAAD,EAAI,CAAJ,EAAO,CAAP,EAAU,CAAV,EAAa,CAAb;;EAGP,IAAA,GACE;IAAA,IAAA,EAAQ,IAAI,CAAC,IAAb;IACA,MAAA,EAAQ,MADR;IAEA,IAAA,EAAQ,SAAC,CAAD;aAAO,CAAA,GAAI,MAAA,CAAO,CAAP;IAAX,CAFR;;;EAKF,IAAA,GAAO,SAAA;AACL,QAAA;IADM,uBAAQ;WACd,KAAA,CAAM,MAAN,EAAc,OAAd;EADK;;EAIP,IAAsB,8CAAtB;IAAA,KAAA,CAAM,YAAN,EAAA;;;EAGA,KAAA;;AAAS;SAAA,sCAAA;;mBAAA,IAAI,CAAC,IAAL,CAAU,GAAV;AAAA;;;AA1BT", - "sources" => ["main.source.coffee"], + "sources" => ["main.coffee"], + "sourcesContent"=>[ + "# Assignment:\n" + + "number = 42\n" + + "opposite = true\n" + + "\n" + + "# Conditions:\n" + + "number = -42 if opposite\n" + + "\n" + + "# Functions:\n" + + "square = (x) -> x * x\n" + + "\n" + + "# Arrays:\n" + + "list = [1, 2, 3, 4, 5]\n" + + "\n" + + "# Objects:\n" + + "math =\n" + + " root: Math.sqrt\n" + + " square: square\n" + + " cube: (x) -> x * square x\n" + + "\n" + + "# Splats:\n" + + "race = (winner, runners...) ->\n" + + " print winner, runners\n" + + "\n" + + "# Existence:\n" + + "alert \"I knew it!\" if elvis?\n" + + "\n" + + "# Array comprehensions:\n" + + "cubes = (math.cube num for num in list)\n" + ], "names" => [], "x_sprockets_linecount"=>47 } @@ -166,9 +201,7 @@ def get_sources(map) assert_equal fixture_path('source-maps/babel/main.es6'), asset.filename assert_equal "babel/main.js.map", asset.logical_path assert_equal "application/js-sourcemap+json", asset.content_type - assert_equal [ - "file://#{fixture_path_for_uri('source-maps/babel/main.es6')}?type=application/ecmascript-6&pipeline=source" - ], normalize_uris(asset.links) + assert_equal [], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ @@ -181,7 +214,37 @@ def get_sources(map) "version" => 3, "file" => "babel/main.es6", "mappings" => ";;;;;;;;;;AACA,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,UAAA,CAAC;SAAI,CAAC,GAAG,CAAC;CAAA,CAAC,CAAC;AACjC,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,UAAC,CAAC,EAAE,CAAC;SAAK,CAAC,GAAG,CAAC;CAAA,CAAC,CAAC;;IAEhC,WAAW;YAAX,WAAW;;AACJ,WADP,WAAW,CACH,QAAQ,EAAE,SAAS,EAAE;0BAD7B,WAAW;;AAEb,+BAFE,WAAW,6CAEP,QAAQ,EAAE,SAAS,EAAE;GAE5B;;eAJG,WAAW;;WAKT,gBAAC,MAAM,EAAE;AACb,iCANE,WAAW,wCAME;KAChB;;;WACmB,yBAAG;AACrB,aAAO,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;KAC5B;;;SAVG,WAAW;GAAS,KAAK,CAAC,IAAI;;AAapC,IAAI,SAAS,uBACV,MAAM,CAAC,QAAQ,0BAAG;MACb,GAAG,EAAM,GAAG,EAEV,IAAI;;;;AAFN,WAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC;;;AAEd,YAAI,GAAG,GAAG;;AACd,WAAG,GAAG,GAAG,CAAC;AACV,WAAG,IAAI,IAAI,CAAC;;eACN,GAAG;;;;;;;;;;;CAEZ,EACF,CAAA", - "sources" => ["main.source.es6"], + "sources" => ["main.es6"], + "sourcesContent"=>[ + "\n" + + "var odds = evens.map(v => v + 1);\n" + + "var nums = evens.map((v, i) => v + i);\n" + + "\n" + + "class SkinnedMesh extends THREE.Mesh {\n" + + " constructor(geometry, materials) {\n" + + " super(geometry, materials);\n" + + "\n" + + " }\n" + + " update(camera) {\n" + + " super.update();\n" + + " }\n" + + " static defaultMatrix() {\n" + + " return new THREE.Matrix4();\n" + + " }\n" + + "}\n" + + "\n" + + "var fibonacci = {\n" + + " [Symbol.iterator]: function*() {\n" + + " var pre = 0, cur = 1;\n" + + " for (;;) {\n" + + " var temp = pre;\n" + + " pre = cur;\n" + + " cur += temp;\n" + + " yield cur;\n" + + " }\n" + + " }\n" + + "}\n" + ], "names" => [], "x_sprockets_linecount"=>66 } @@ -226,9 +289,7 @@ def get_sources(map) assert_equal fixture_path('source-maps/sass/main.scss'), asset.filename assert_equal "sass/main.css.map", asset.logical_path assert_equal "application/css-sourcemap+json", asset.content_type - assert_equal [ - "file://#{fixture_path_for_uri('source-maps/sass/main.scss')}?type=text/scss&pipeline=source" - ], normalize_uris(asset.links) + assert_equal [], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ @@ -241,7 +302,24 @@ def get_sources(map) "version" => 3, "file" => "sass/main.scss", "mappings" => "AAAA,AACE,GADC,CACD,EAAE,CAAC;EACD,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,UAAU,EAAE,IAAI,GACjB;;AALH,AAOE,GAPC,CAOD,EAAE,CAAC;EAAE,OAAO,EAAE,YAAY,GAAI;;AAPhC,AASE,GATC,CASD,CAAC,CAAC;EACA,OAAO,EAAE,KAAK;EACd,OAAO,EAAE,QAAQ;EACjB,eAAe,EAAE,IAAI,GACtB", - "sources" => ['main.source.scss'], + "sources" => ['main.scss'], + "sourcesContent"=>[ + "nav {\n" + + " ul {\n" + + " margin: 0;\n" + + " padding: 0;\n" + + " list-style: none;\n" + + " }\n" + + "\n" + + " li { display: inline-block; }\n" + + "\n" + + " a {\n" + + " display: block;\n" + + " padding: 6px 12px;\n" + + " text-decoration: none;\n" + + " }\n" + + "}\n" + ], "names" => [], "x_sprockets_linecount"=>12 } @@ -267,10 +345,7 @@ def get_sources(map) assert_equal fixture_path('source-maps/sass/with-import.scss'), asset.filename assert_equal "sass/with-import.css.map", asset.logical_path assert_equal "application/css-sourcemap+json", asset.content_type - assert_equal [ - "file://#{fixture_path_for_uri('source-maps/sass/_imported.scss')}?type=text/scss&pipeline=source", - "file://#{fixture_path_for_uri('source-maps/sass/with-import.scss')}?type=text/scss&pipeline=source" - ], normalize_uris(asset.links) + assert_equal [], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ @@ -284,8 +359,14 @@ def get_sources(map) "file" => "sass/with-import.scss", "mappings" => "ACAA,AAAA,IAAI,CAAC;EAAE,KAAK,EAAE,GAAG,GAAI;;ADErB,AAAA,GAAG,CAAC;EAAE,KAAK,EAAE,IAAI,GAAI", "sources" => [ - "with-import.source.scss", - "_imported.source.scss", + "with-import.scss", + "_imported.scss", + ], + "sourcesContent" => [ + "@import 'imported';\n" + + "\n" + + "nav { color: blue; }\n", + "body { color: red; }\n" ], "names" => [], "x_sprockets_linecount"=>5 @@ -322,23 +403,38 @@ def get_sources(map) test "source maps work with index alias" do asset = @env.find_asset("foo.js", pipeline: :debug) mapUrl = asset.source.match(/^\/\/# sourceMappingURL=(.*)$/)[1] - assert_equal "foo/index.js-008b5ccb5459dc75d7fd51bf5b1ac79fe54d05157d50586c16e558f33d28e9c4.map", mapUrl + assert_equal "foo/index.js-0537e98484a750b47c100f542e6992f7f2fbc09b355f47f1206a9d3adffb65ce.map", mapUrl map = JSON.parse(@env.find_asset('foo/index.js.map').source) assert_equal [ - "file.source.coffee", - "index.source.js" + "file.coffee", + "index.js" ], get_sources(map) + assert_equal [ + "console.log(\"foo/file.coffee\") if 1 < 2\n", + "//= require ./file\n" + + "console.log(\"foo.js\");\n" + ], get_sourcesContent(map) end test "relative sources at different depths" do assert @env.find_asset("sub/directory.js", pipeline: :debug) assert map = JSON.parse(@env.find_asset("sub/directory.js.map").source) assert_equal [ - "a.source.js", - "modules/something.source.js", - "directory.source.js" + "a.js", + "modules/something.js", + "directory.js" ], get_sources(map) + assert_equal [ + "function a() {\n" + + " console.log('sub/a.js') \n" + + "}\n", + "console.log(\"something.js\")\n", + "//= require ./a\n" + + "//= require ./modules/something\n" + + "console.log('sub/directory.js');\n" + + "a();\n" + ], get_sourcesContent(map) end test "source maps are updated correctly after file change" do @@ -350,6 +446,7 @@ def get_sources(map) map["sections"][(index+1)..-1].each do |s| s["offset"]["line"] += 1 end + map["sections"][index]["map"]["sourcesContent"][0] << "console.log('newline');\n" end File.open(filename, 'a') do |file| @@ -390,9 +487,7 @@ def setup assert_equal fixture_path('source-maps/sass/main.scss'), asset.filename assert_equal "sass/main.css.map", asset.logical_path assert_equal "application/css-sourcemap+json", asset.content_type - assert_equal [ - "file:///#{ fixture_path('source-maps/sass/main.scss').delete_prefix('/') }?type=text/scss&pipeline=source" - ], normalize_uris(asset.links) + assert_equal [], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ @@ -405,7 +500,24 @@ def setup "version" => 3, "file" => "sass/main.scss", "mappings" => "AAAA,AACE,GADC,CACD,EAAE,CAAC;EACD,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,UAAU,EAAE,IAAI,GACjB;;AALH,AAOE,GAPC,CAOD,EAAE,CAAC;EAAE,OAAO,EAAE,YAAY,GAAI;;AAPhC,AASE,GATC,CASD,CAAC,CAAC;EACA,OAAO,EAAE,KAAK;EACd,OAAO,EAAE,QAAQ;EACjB,eAAe,EAAE,IAAI,GACtB", - "sources" => ["main.source.scss"], + "sources" => ["main.scss"], + "sourcesContent"=>[ + "nav {\n" + + " ul {\n" + + " margin: 0;\n" + + " padding: 0;\n" + + " list-style: none;\n" + + " }\n" + + "\n" + + " li { display: inline-block; }\n" + + "\n" + + " a {\n" + + " display: block;\n" + + " padding: 6px 12px;\n" + + " text-decoration: none;\n" + + " }\n" + + "}\n" + ], "names" => [], "x_sprockets_linecount"=>12 } @@ -431,10 +543,7 @@ def setup assert_equal fixture_path('source-maps/sass/with-import.scss'), asset.filename assert_equal "sass/with-import.css.map", asset.logical_path assert_equal "application/css-sourcemap+json", asset.content_type - assert_equal [ - "file://#{fixture_path_for_uri('source-maps/sass/_imported.scss')}?type=text/scss&pipeline=source", - "file://#{fixture_path_for_uri('source-maps/sass/with-import.scss')}?type=text/scss&pipeline=source" - ], normalize_uris(asset.links) + assert_equal [], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ @@ -448,8 +557,14 @@ def setup "file" => "sass/with-import.scss", "mappings" => "ACAA,AAAA,IAAI,CAAC;EAAE,KAAK,EAAE,GAAG,GAAI;;ADErB,AAAA,GAAG,CAAC;EAAE,KAAK,EAAE,IAAI,GAAI", "sources" => [ - "with-import.source.scss", - "_imported.source.scss" + "with-import.scss", + "_imported.scss" + ], + "sourcesContent" => [ + "@import 'imported';\n" + + "\n" + + "nav { color: blue; }\n", + "body { color: red; }\n" ], "names" => [], "x_sprockets_linecount"=>5 From cb72ae4a38eb9e220a46b3bc28dff211e1dc2919 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 2 Nov 2020 15:54:43 +0900 Subject: [PATCH 2/2] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index efac5a1b0..11c3c484a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Get upgrade notes from Sprockets 3.x to 4.x at https://github.com/rails/sprocket ## Master - Remove remaining support for Ruby < 2.4.[#672](https://github.com/rails/sprockets/pull/672) +- Embed sourcesContent in source maps. [#697](https://github.com/rails/sprockets/pull/697) ## 4.0.2