Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

source-map omits some mappings when regenerating source map #252

Open
x-yuri opened this issue Nov 26, 2016 · 2 comments
Open

source-map omits some mappings when regenerating source map #252

x-yuri opened this issue Nov 26, 2016 · 2 comments
Labels
feat New feature

Comments

@x-yuri
Copy link

x-yuri commented Nov 26, 2016

See next post for a lower level case.

I hope you don't mind me explaining how to reproduce the issue with webpack:

package.json:

{
  "dependencies": {
    "css-loader": "^0.26.0",
    "extract-text-webpack-plugin": "^1.0.1",
    "html-webpack-plugin": "^2.24.1",
    "node-sass": "^3.13.0",
    "sass-loader": "^4.0.2",
    "style-loader": "^0.13.1",
    "webpack": "^1.13.3"
  }
}

webpack.config.js:

var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    entry: './1.js',
    output: {
        path: 'dist',
        filename: 'bundle.js',
    },
    module: {
        loaders: [
            {test: /\.scss$/, loader: ExtractTextPlugin.extract('style', 'css?sourceMap&minimize!sass?sourceMap')},   // (1)
            // {test: /\.scss$/, loaders: ['style', 'css?sourceMap&minimize!sass?sourceMap']},   // (2)
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'template.ejs',
        }),
        new ExtractTextPlugin('[name].css'),   // (1)
    ],
    devtool: 'source-map',   // (1)
};

template.ejs:

<!doctype html>
<html>
<body>

<div>
    <div></div>
</div>

</body>
</html>

1.js:

require('./1.scss');

1.scss:

div {
    width: 10px;
    div {
        width: 20px;
    }
}
$ npm i
$ rm -rf dist/* && ./node_modules/.bin/webpack

With extract-text-webpack-plugin (1) you get source map in dist/main.css.map and the following mappings:

AAAA,IACI,UAAY,CAIf,QAFO,UAAY

([0,0](#0)=>[0,0])
    | ([1,4](#0)=>[0,4])
    | ([1,16](#0)=>[0,14])
    | ([5,1](#0)=>[0,15])
    | ([3,8](#0)=>[0,23])
    | ([3,20](#0)=>[0,33])

And if you open the page in Chrome (http://example.com/dist), open Developer Tools > Elements tab. Click on inner div, you'll see:

extract-text-webpack-plugin

Now, if you comment out lines marked with (1), and uncomment the one marked with (2), run webpack, you'll get the following mappings (in bundle.js, search for word mappings):

AAAA,IACI,UAAY,CAIf,AALD,QAGQ,UAAY,CACf

([0,0](#0)=>[0,0])
    | ([1,4](#0)=>[0,4])
    | ([1,16](#0)=>[0,14])
    | ([5,1](#0)=>[0,15])
    | ([0,0](#0)=>[0,15])   // source-map removes this
    | ([3,8](#0)=>[0,23])
    | ([3,20](#0)=>[0,33])
    | ([4,5](#0)=>[0,34])   // and this lines

and the following line in Chrome:

no-extract-text-webpack-plugin

Which is arguably correct. Anyways, line 6 doesn't even come close to it.

Now, if you change node_modules/webpack/lib/SourceMapDevToolPlugin.js like so:

--- node_modules/webpack/lib/SourceMapDevToolPlugin.js  2016-11-26 20:37:53.178777196 +0200
+++ node_modules/webpack/lib/SourceMapDevToolPlugin.js.new      2016-11-26 20:37:51.612119017 +0200
@@ -52,7 +52,16 @@
                                                return;
                                        }
                                        if(asset.sourceAndMap) {
+if (file == 'main.css') {
+console.log('number of children: ', asset.children.length);
+console.log(asset.children[0]._sourceMap.mappings);
+console.log(options);
+}
                                                var sourceAndMap = asset.sourceAndMap(options);
+if (file == 'main.css') {
+console.log('source changed: ', asset.children[0].source() != sourceAndMap.source);
+console.log(sourceAndMap.map.mappings);
+}
                                                var sourceMap = sourceAndMap.map;
                                                var source = sourceAndMap.source;
                                        } else {

you'll see:

number of children:  1
AAAA,IACI,UAAY,CAIf,AALD,QAGQ,UAAY,CACf
{ filename: '[file].map[query]',
  moduleFilenameTemplate: undefined,
  fallbackModuleFilenameTemplate: undefined,
  append: null,
  module: true,
  columns: true,
  lineToLine: false,
  noSources: false,
  test: /\.(js|css)($|\?)/i }
source changed:  false
AAAA,IACI,UAAY,CAIf,QAFO,UAAY

sourceAndMap() function is defined here. And here's where it passes control to source-map package.

Having that said, we can see that source map was changed, for no clear reason. Can you explain it? This seems like a bug in source-map.

Files needed to reproduce it with Webpack 3

@x-yuri
Copy link
Author

x-yuri commented Jan 27, 2018

I've just reinspected the test case. It all boils down to the following code:

var sourceMap = require('source-map');   // 0.5.7
var generatedCode = 'div{width:10px}div div{width:20px}';
var sm = 
{ version: 3,
  sources:
   [ '../../../../css-loader!/srv/http/s1/source-map-252-2/1.scss' ],
  names: [],
  mappings: 'AAAA,IACI,UAAW,CAId,AALD,QAGQ,UAAW,CACd',
  file: '1.scss',
  sourcesContent:
   [ 'div {\n    width: 10px;\n    div {\n        width: 20px;\n    }\n}\n' ] };
console.log(generatedCode);
console.log(sm.mappings);

var r = sourceMap.SourceNode
    .fromStringWithSourceMap(generatedCode, new sourceMap.SourceMapConsumer(sm))
    .toStringWithSourceMap();
console.log(r.code);
console.log(r.map.toJSON().mappings);

In the original source map there are empty code fragments (part of code between two mappings), which results in source nodes with no children.

And as a result, no children, no mappings.

I'm not sure if this is okay, or not. The spec is not strict (what I can understand). Everybody has their own idea of what is acceptable. So feel free to close the PR.

For browsers these days it doesn't matter much. They learned to use the mapping after the opening bracket (div div{), from what I can gather.

@x-yuri x-yuri changed the title source-map changes source map when it probably shouldn't source-map omits some mappings when regenerating source map Jan 27, 2018
@jkrems jkrems added the feat New feature label Apr 18, 2021
@jkrems
Copy link
Collaborator

jkrems commented Apr 18, 2021

Agreed that it's confusing that the roundtrip between .fromString and .toString is lossy. Thanks for the clear test case!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat New feature
Projects
None yet
Development

No branches or pull requests

2 participants