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

Places incorrect paths in extracted source maps for input that has relative sources #1225

Open
rjgotten opened this issue Jun 17, 2019 · 15 comments

Comments

@rjgotten
Copy link

  • Operating System: Windows 10
  • Node Version: 10.15.3
  • NPM Version: 6.9.0
  • webpack Version: 4.31.0
  • mini-css-extract-plugin Version: 0.7.0

Expected Behavior

When offered compiled CSS content backed by a source map where sources are relative URLs, extracts a .css file with a source map that has correct path information. That is: the sources are resolved against this.context of the compiled CSS content, producing the correct source locations.

Actual Behavior

When offered compiled CSS content backed by a source map where sources are relative URLs, extracts a css file with a source map that has incorrect path information. The sources seem to be output as-is to be interpreted by debuggers and dev-tools as relative to the extracted CSS file. This produces incorrect source locations.

Code

N/A

How Do We Reproduce?

Minimal repro case:

https://github.com/NetMatch/mini-css-extract-plugin-testcase

@rjgotten
Copy link
Author

rjgotten commented Jun 17, 2019

Somehow, I think this all ties back into the fact that Webpack itself wants disk-based - i.e. filesystem - absolute paths in source map sources whereas according to specification those are supposed to be URLs.

When said URLs are relative, they are relative to the generated file. Most external supplied source maps are relative, because that's the only way they can be portable.


Assuming Webpack is not going to budge and fix this mess, a work-around is needed where source map sources are eventually made compatible with Webpack's counter-spec status quo.

This is complicated for a few reasons, mostly because there's a lot of moving parts capable of influencing the source map for CSS: less-loader; sass-loader; postcss-loader; css-loader; mini-css-extract-plugin and possibly more.

Both postcss-loader and css-loader internally use PostCSS. This must receive proper (relative) URL sources in the map, or it produces bad paths in its output sources. Usually you ge especially bad results on Windows, where the path separator for filesystem paths is \ and not / making a further mess of things.

A custom loader module can be written to normalize any unfit source maps before they are offered to either of these loaders. However, specifically for css-loader there is no opportunity to rewrite its outmap maps into the disk-based absolute paths that core Webpack expects, as css-loader emits a JS module with the CSS source map encapsulated in it.

It and css-mini-extract-plugin following it in the loader chain, are opaque to end-users. I.e. the issue will need to be worked around from inside the plugin (and loader).

@alexander-akait
Copy link
Member

PR welcome, maybe need fix in css loader first

@rjgotten
Copy link
Author

rjgotten commented Jun 17, 2019

@evilebottnawi

I'm now running a small experiment, using esprima and escodegen to parse the JS module output by css-loader; re-transform the relative URLs in its embedded CSS source-map to absolute disk paths; and then re-emit the corrected module.

Sofar, that seems to work well with the MiniCssExtractPlugin as well. If this holds up, I'll consider building up a pull request with a real fix. (There's some performance-related motivation there for me to provide an integrated fix. Because of course; using esprima for outside manipulation is not cheap. ;-) )

@alexander-akait
Copy link
Member

Anyway feel free to put here any information and feedback

@alexander-akait
Copy link
Member

Partial fixed for latest css-loader/mini-css-extract-plugin/webpack versions. Just note:

  • webpack:./style/content.less should be webpack:///./style/content.less

@alexander-akait
Copy link
Member

Now you will have "sources":["webpack:///./style/content.less","webpack:///./style/content.css"], it is hard to say css-loader what we should use content.less in from, because PostCSS doesn't support multiple values in the from option

@alexander-akait
Copy link
Member

I want to move this issue to css-loader, because nothing to do here, we should found a way to say css-loader/postcss-loader using right source from source-map-loader

@alexander-akait alexander-akait transferred this issue from webpack-contrib/mini-css-extract-plugin Nov 6, 2020
@alexander-akait
Copy link
Member

@rjgotten Do you have ideas how we can achieve this?

@rjgotten
Copy link
Author

rjgotten commented Nov 7, 2020

Maybe just cop out? Introduce a rule that says the first candidate matching a given predicate test is passed on.
Or introduce a system which favors one extension type over the other?

It's situational, I think. So probably a user specified predicate/test regex or function is the better deal.

Then rewrite the source-map to only contain those matching sources, in situations where multiple are provided.

@alexander-akait
Copy link
Member

@rjgotten We can't rewrite source maps, PostCSS does it, we just provide from/to and previously source map

@rjgotten
Copy link
Author

rjgotten commented Nov 9, 2020

Ah sorry, about the confusion there.

I didn't mean "rewrite the source-map" as in creating a chained source map, which indeed is what PostCSS should be doing.
I meant to rewrite the sources attribute of the source-map that is passed to PostCSS, so it only shows one source to PostCSS.

Thinking further though: why is the presence of multiple sources in the incoming map even an issue for the from option?
The PostCSS from and to options are the input and output CSS being processed.
And iirc the only connection from has to source-maps is that the file name provided there will be used as the content of a source-map's sources in the event that PostCSS starts a new source map - i.e. when it is not chaining off of an existing one. In case of an incoming source-map that already has multiple sources, that case obviously doesn't - or at least: shouldn't - apply.

@alexander-akait
Copy link
Member

We can pass to from first source (map.sources[0]) if we have it in source map, so we can achieve this

@rjgotten
Copy link
Author

rjgotten commented Nov 9, 2020

Oh; right. Now I get what the problem is.
For compatibility's sake, you need to set a name for the source, because you're passing it in programmatically and not from disk even though you're chaining off of an existing source map. (Yeah; that took a moment to click with me... sorry.)

Sure; in that case just going with whatever is listed as the first entry in map.sources should work.
Heck, just passing in any arbitrary string should work, I guess, because it should never end up in the output source-map. The output source-map should be propagating the original sources from the input source-map, after all.

@alexander-akait
Copy link
Member

Yep, I am worried that some of the tools may be generating incorrect invalid order of sources, but I think this is not our problem

@nineninesevenfour
Copy link

Hi! I am new to webpack and css-loader, but I want to share my observations and hope it helps, but if its not relevant for this issue, please ignore it. thx. 😉
While going through the "Asset Management" tutorial (and playing around with source maps) I ran webpack with the --stats verbose option and get the following output:

ModuleConcatenation bailout: Cannot concat with ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[0].use[1]!./src/style.css: Module uses module.id

On Stackoverflow I can read the following about it:

...Webpack receives the CommonJS modules, but the ModuleConcatenationPlugin relies on ES modules....

I found no option to change the internal behaviour of css-loader from CJS towards ES6 modules.
Funnily Firefox does not display me the style.css at all, while in Safari I get two style.css: one is the js-output of css-loader and one is the js-output of style-loader. Obviously somehow the css source map resolves to an intermediate result instead of the original one. And I suppose it might be due to a simple incompatibilty with the bundled ModuleConcatenationPlugin.

Btw. I removed babel-loader from the webpack.config.js to get sure that is not intervening that process.

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

No branches or pull requests

3 participants