-
-
Notifications
You must be signed in to change notification settings - Fork 8.7k
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
Webpack is producing invalid sourcemaps? #8302
Webpack is producing invalid sourcemaps? #8302
Comments
I'm observing the same issue as well. |
Reposting over here - sorry about the delay on this. I ran through my sample repo (https://github.com/kenhoff/webpack-sourcemap-testing) and updated to the latest versions of everything. Appears to still be an issue. |
@kenhoff thanks, need investigate who break source map 😕 |
Just for the record. We have found out that in our case issue was in the optimizations CDN did to JS bundles before sending them to the clients. So source code didn't match the map. Though the above issue still exists and source map validation fails. |
Invalid sourcemaps with v4.28.1 |
Is this issue the root cause of facebook/create-react-app#6296 ? That issue is caused by whitespace differences (newlines and indentation only) between sourcemap source code and on-disk source code. |
Any updates on this? Still get invalid sourcemaps with v4.29.6 |
Invalid source-maps with 4.29.6 and 4.30.0. devtool: 'source-map' Although it doesn't seem to have anything to do with babel, since I get invalid source maps (wrong line number) even when removing babel completely. Edit: |
Also an issue for me!
|
This issue is actually preventing me from launching my Firefox addon since I need to send sourcemaps to Mozilla for review. I have read the thread about the Facebook React apps as mentioned by @justingrant and tried the suggestions there with no luck. In desperation, I searched for all instances of "var installedModules" inside the node_modules directory, and deleted any whitespace character (starting with spaces and tabs, then removing the /*****/). I tested by building and validating sourcemap after each file change. I found NO change in the result despite having changed every file I came across! I have since concluded that this issue centres around the file MainTemplate.js (and associated files/classes/methods) (node_modules/webpack/lib/MainTemplate.js) - there is something that it is outputting that the sourcemap generator cannot deal with correctly. Changing line 427 does have an effect on the issue:
(Notice the line "Got: var installedMod...") Still doesn't solve the issue, but gets a little closer. I suspect that the real issue here is not in this file, rather how the output is being parsed by the source map generator to produce the mappings.... Apologies a rather layman approach, but I am no expert in this area and am desperate to find a solution - I'm suprised this is not crippling others? |
FYI, the problems I was running into with React turned out to be fixed by changing Babel configuration. If you configure Babel with If you change Babel config to use facebook/create-react-app#7022 has more details about the problem and the solution. The TL;DR is that if Babel is used, then Unfortunately, I can't comment about the variable name mapping issues that @september28 is running into above, because create-react-app sourcemaps don't even include |
OK, I have done some more investigation which I will share below. Would be really grateful if someone from the Webpack team could take a look at this issue since it would seem pretty fundamental to most projects? OK, so my thinking is that this issue has to do with the So I began by "overriding" the minify function of the Terser Plugin. Here's the relevant section of my webpack.config.js:
As you can see, my minify function is just console.logging the two parameters that have been passed to it. My first observation is that the "file" code is different from the sourceMap's "sourcesContent" code - perhaps the first indicator that something is not quite right? (Abbreviated) file param:
(Abbreviated) sourceMap param:
Now, even though this doesn't quite make sense to me (that file code should be different to the sourceMap sourcesContent), The minifier is going to completely change this file code anyway, so it will have to create a new source map for the code. I took a look at how the TerserPlugin does this. node_modules/terser-webpack-plugin/minify.js calls terser's minify function with the file content, and a terserOptions object. inside that object, the sourceMap has been injected:
node_modules/terser/dist/bundle.js defines the minify function. Around line 20671 seems to be the relevant source map code:
so, the original sourceMap (from, presumably, the webpack loader or whatever part of webpack that "feeds" the minimize function) gets converted into a JSON object, and then a SourceMap is instantiated using the original source map content. the SourceMap class is defined earlier in the same bundle.js file, and commented as "a small wrapper around fitzgen's source-map library". It's basically a wrapper around MOZ_SourceMap. It looks like there is a node_modules sub-heirarchy inside the terser package, so I think the SourceMapGenerator class is defined in node_modules/terser/node_modules/source-map/lib/source-map-generator.js. So up to this point we know that the sourceMap mappings are going to be incorrect since the code has been minified/mangled. There is an OutputStream class that is called in the minify function which handles the "rewrite" of the sourcemap mappings.
So this do_add_mapping method calls the "add" method from the SourceMap class (wrapper):
So I think the important thing here is the conditional interestingly the orig_line and orig_col passed to this add method look different (indeed more correct) - I think they have been generated when we created the SourceMap (see above code snippet): Looking in the source-map-generator.js file, the addMapping function:
So this function does not seem to remove mappings? So Is it the case that we are just adding to the eroneous mappings that we know already exist in the source map? So my investigation is now going to focus on how the sourceMap is generated that is passed to the terser minify function - I believe this is generated during "compilation" either by a loader, or perhaps by the SourceMapDevToolPlugin (it is certainly called before the minify function). Any help greatly appreciated!! As a side note, the version of the source map library that terser seems to be using is fairly old now (0.5.6) - this is probably because, after that point, there were some fairly major changes made to Promiseify the code etc - ChangeLog |
@september28 - thanks for sharing. Haven't fully dived into your findings, but I wanted to share some work I did on source map handling in Terser: terser/terser#298. Maybe it doesn't apply - haven't looked closely. With your setup, could you try using this branch w/ the "auto" feature? Or, if you could upload a repo of your setup I could (it's been awhile since I've setup any webpack :) ) |
Hi @hoten thanks for the reply - however I don't think your work on terser/terser#298 is relevant to the issue. Correct me if I'm wrong, but your mods seem to address an issue with finding sourcemaps from the #SourceMappingURL comment, or searching for a file.map.js if the special comment doesn't exist? |
I just tried the same scripts installed on a Windows 10 machine to rule out the system architecture. Still no joy. Is anyone from Webpack tracking this issue? It is pretty fundamental to JS projects in Webpack... |
OK so I think this I have isolated this down to the way that webpack generates sourcemaps for "required" files when bundling. I changed the sourcemap-validator code (node_modules/sourcemap-validator/index.js) around line 146:
Basically I stopped the code from throwing an exception, opting instead for a console.log message. Originally I just did a
There were so many that my terminal window ran out of buffer so I could not analyse them all. I then added the conditional in the above code to see if there were any errors in the actual js file that I had written, since this would have been transpiled/minified by webpack (probably at a separate stage) and then concatenated into the final minified file (along with the other "required"-in modules). There are no validation errors shown for anything other than the webpack:// "files", which leads me to add more weight towards my theory that there is something going wrong with webpack when the required modules are being transpiled/minified and sourcemaps are being written. |
After some more digging, it would seem that the "original" column mappings are not happening correctly. I have liberally sprinkled console.log statements in: "node_modules/webpack-sources/node_modules/source-map/lib/source-node.js", specifically in the |
I believe column mapping is configurable in source map generation (maps are smaller if you don't embed column info). Maybe there's a misconfiguration in some tool? @september28 I've set aside time this weekend to dive deeper into this topic. It's bugged me for a long time too. |
Hi @hoten , yep column mapping is configurable. In my examples options.columns passed to node function in OriginalSource.js is always |
Okay, I've got some news. Let me describe my debugging approach first. My config: const path = require('path');
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
devtool: 'source-map'
};
I use
Change
Add Click to expand // The module cache
var installedModules = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// define getter function for harmony exports
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};
// define __esModule on exports
__webpack_require__.r = function(exports) {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
// create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
__webpack_require__.t = function(value, mode) {
if(mode & 1) value = __webpack_require__(value);
if(mode & 8) return value;
if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, 'default', { enumerable: true, value: value });
if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
return ns;
};
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = 0);
I set a conditional breakpoint in if (mapping.token.value === 'installedModules') debugger; The token I want to see how this token / AST is made, so I step up a bit and see where Terser parses it. I added a debugger statement around L20578, to inspect the AST and confirm nothing is modifying it between here and
Also 14, which suggests I should stop looking inside Terser for the issue. I notice that the file input has a bunch of Click to expand"/******/ (function(modules) { // webpackBootstrap
/******/// The module cache
/******/var installedModules = {};
/******/
/******/// The require function
/******/function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/}
/******/
/******/
/******/// expose the modules object (__webpack_modules__)
/******/__webpack_require__.m = modules;
/******/
/******/// expose the module cache
/******/__webpack_require__.c = installedModules;
/******/
/******/// define getter function for harmony exports
/******/__webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/};
/******/
/******/// define __esModule on exports
/******/__webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/};
/******/
/******/// create a fake namespace object
/******/// mode & 1: value is a module id, require it
/******/// mode & 2: merge all properties of value into the ns
/******/// mode & 4: return value when already ns object
/******/// mode & 8|1: behave like require
/******/__webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/};
/******/
/******/// getDefaultExport function for compatibility with non-harmony modules
/******/__webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/};
/******/
/******/// Object.prototype.hasOwnProperty.call
/******/__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/// __webpack_public_path__
/******/__webpack_require__.p = "";
/******/
/******/
/******/// Load entry module and return exports
/******/return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
console.log(123);
/***/ })
/******/ ]); A search for Consider this code: Perhaps the issue lies with how Next, I remove all the extraneous prefixes. If the original code is just: var installedModules = {}; then I'd expect the original column to be 4 (or 5?). I remove the I remove the I start trashing some stuff. I minimize the
I print out the map here ( Remember when I said I stopped looking for the issue in Terser? Well, I guess that's busted now. I'll take a break at this point :) |
@hoten Great progress... Seems like you have found broadly the same as me so far. One comment I have about whether the issue lies with Webpack or Terser/Terser-webpack-plugin is that I have observed the following:
Still not sure how much relevance this has, but thought I would share. |
It's 100F+ in the SF bay area, I have no A/C, and stumbling around this issue some more seems preferable to attempting to sleep :)
OK, this part was nonsense. I didn't get validation errors using Which looks totally fine visualized: Visualizing the output of my window.blah=[function(o,n){console.log(123)}];
//# sourceMappingURL=bundle.js.map {"version":3,"sources":["webpack:///./index.js"],"names":["console","log"],"mappings":"2BAAAA,QAAAC,IAAA","file":"bundle.js","sourcesContent":["console.log(123);\n"],"sourceRoot":""} Each of these mappings are mapped to the first column, which goes back to what @september28 said earlier. We should look closer at how tokens are mapped to columns inside Terser. I thought Terser might have trouble with composing source maps, but I ruled that out when this change to the Terser plugin still presented the error: |
This is suspicious: This method is called by
And the map that accompanies it is correct:
So why can't Terser find line 5 column 8? I can - look! This brings me to, what I hope, is our final stop on the River Styx (please allow me to be literary, source maps are driving me crazy). I set a breakpoint here: and dig into ...or does it? We've come this far, and (well, at least for me) with very little understanding of how source maps actually work. Why learn now? Ok I actually did learn a bit. So now ... let's figure out why the map created via |
Could you check if upgrading webpack-sources to 1.4.1 and terser-webpack-plugin to 1.4.1 helps with this issue? |
It works when there's just some simple code in the entry file, but seems to break for any require. Test repo: https://github.com/connorjclark/webpack-sourcmap-validator-names-test
|
/cc @sokra |
OK, I tried to use latest webpack version and debug with sourcemaps in production mode.
webpack.config.js:
But in my case, breakpoints are still set incorrectly (IDE is VS Code). If I set mode: "development", everything works as expected. Have a look at this sample repository. Did I miss something or is there still an issue with sourcemaps? |
/cc @sokra looks problem still exists 😞 |
@ford04 This behavior seem to be correct, as these function were inlined, so the line you want to set a breakpoint on doesn't exist in the production optimized code: Your code get optimized to this: "use strict";
o.r(t);
const r = () => {
setTimeout(() => {
console.log("Hello arrow");
}, 1e3);
};
console.log("Hello world"),
r(),
console.log("got me."); |
@ford04 unfortunately any non trivial code transformations (read: inlining) don't get mapped well. Blame the spec on that one :/ |
Honestly, in terms of developer/debugging experience, this behavior is very confusing, as you don't know which code parts have been optimized and where you can set your breakpoint after all. No experience on source map processing, but I would expect that my source code line 15, column 1 in Update: thanks @connorjclark for the info. Didn't know that the specs are so vague. So there is no solution to this? Would mean to me, that sourcemaps in production are nonsense. |
I don't know if it's impossible (I'm not very familiar with uglifyjs's codemods), but at the very least it's a combination of insufficient spec + practicality.
I wouldn't say that. Yea, some places don't get mapped well, but the majority does. I know that in Chrome DevTools, the unmappable lines are faded (last I checked, that was not the case in FF DevTools) - which does help a lot. If you attempt to set a breakpoint on such a line in Chrome DevTools, we do a best-attempt at placing it elsewhere. EDIT: You may be interested in some notes of mine re: source maps and their limitations. |
Thank you! I'll have a look at it. At least good to know that now sourcemaps are working as intended (or as accurate as the spec permits). |
Fixes an issue where invalid prod mode source maps cause browsers to be unable to open the client when built in prod mode. See webpack/webpack#8302
Fixes an issue where invalid prod mode source maps cause browsers to be unable to open the client when built in prod mode. See webpack/webpack#8302
Fixes an issue where invalid prod mode source maps cause browsers to be unable to open the client when built in prod mode. See webpack/webpack#8302
* Initial rewrite commit using patternfly-react-seed * Initial port of command channel service * Initial port of API service * Very rough auth flow support * Use function components to avoid 'this' binding complications * Add component for (dis)connecting targets * Update Select state on selection * Include target connectUrl in select option label * Add ContainerNode icon to Select * Add title label to Select * Use Grid layout * Allow message ID to be supplied by caller * 'Dynamic' routes only added when target connected * Update label using is-connected listener * Remove default Dashboard and Support pages * Add rudimentary recording table view * Update README * Split Login component into React Form components (#52) Basic login form asks for separate username and password, Bearer form asks for just auth token Additionally, use Card styling * Add TargetSelect widget on RecordingList (#51) * Add TargetSelect on recordings list view * Only allow target disconnection from dashboard select * Do not send messages before command channel is ready * Add precondition to sendMessage ensuring channel is ready before messages processed * Refactor to extract reusable TargetView container component * allowDisconnect prop is optional, default false * Each TargetView child becomes its own GridItem * Dashboard reuses TargetView * Update Route page titles to match components * TargetView uses Stack layout rather than Grid * Wrap TargetSelect contents within Card * Wrap RecordingList in Card * Include target port in render and command args * Send user back to login when command channel connection is lost (#53) * Clean up Patternfly Seed remnants (#62) * Replace Seed app title * Remove unused header "Toolbar" example * "Create Recording" view (#59) * Set up "Create Recording" view routing * Add public function for generating message IDs * Implement custom recording creation form * Render continuous recording duration as Continuous rather than 0s * Nest routes, style nav active if route/child match * Add navigable breadcrumbs replacing page titles * Extract BreadcrumbPage component * Validate recording name Add form property to display invalid style when recording name provided is invalid * Constrain duration input to minimum of 0 * Validate recording event specifier string Add form properties to style event specifier string text-area as invalid if the specifier is invalid * Update Template form input if user manually inputs matching name * Add event specifier string tooltip icon * Add global toast notifications * Inform user of form issues and recording creation status via toast * CommandChannel uses Notifications for user error messages * Reformat tooltip for readability * Recording List overhaul as Data List (#63) * Replace React Table with Data List for flexibility Render recording download and report URLs as downloadable links * Add row checkboxes * Implement button for deleting recordings * Implement button for stopping recordings * Extract toolbar to local function component * Append .jfr to recording download request filename * Enable Stop button only when some selected recording is RUNNING/STARTING * Initial Events view (#54) * Add first-pass impls of Events components * Add event template filtering * Add event-types table pagination * Move event type options into collapsible rows And set event tables to compact styling * Add event type filtering * Apply changes from 'npm run format' * Fix uppercase/lowercase filtering bug * Allow comma-delimited category string matches * Re-apply 'npm run format' * Recalculate page number from previous perPage Maintain state for previous count of items per page and recalculate the page number and displayed slice to keep the current items in view when the user changes the count per page * Correct item count when filter text is non-empty * Use eval-source-map in prod mode (#68) Fixes an issue where invalid prod mode source maps cause browsers to be unable to open the client when built in prod mode. See webpack/webpack#8302 * Prefix request paths with /api/v1 (#66) Related to cryostatio/cryostat-legacy#148 * Add ability to create Snapshot recordings (#67) * Extract custom recordings form component * Add tabset around card body * Add Snapshot recording form * Add "Archived Recordings" view tab (#69) * Extract active recording list component * Add Archived Recordings view Add Archived Recordings view under Recordings List tab, visible/enabled only when the ContainerJFR instance supports archiving * Add "Archive" button to active recordings view Add button allowing users to select active recordings and save them to the archives Fixes #57 * Immediately refresh archived list when save message observed * Refactor to extract shared data list table element * Use PF DataToolbar instead of plain Toolbar * Add action button for creating recordings from template (#70) * Add action button for creating recordings from Event Templates Related to #58 * Restyle toolbars using PF DataToolbar * Do not (un)mount child components on tab selection
Why was this closed? Is this fixed? WHere? |
tl;dr
devtool: "source-map"
, a barebones webpack config, andwebpack@4.23.1
(this might be an issue with sourcemap-validator, but the error that it's throwing (in the "current behavior" section below) seems pretty legit)
Bug report
What is the current behavior?
Running webpack with the following config:
dist/bundle.js
anddist/bundle.js.map
.sourcemap-validator
(or https://sourcemaps.io/, and consequently Sentry) produces the following error:If the current behavior is a bug, please provide the steps to reproduce.
Repro repo here: https://github.com/adaniliuk/webpack-sourcemap-testing
Repro instructions for that repo are on the readme.
If starting from scratch, use the following webpack config:
and use
sourcemap-validator
to testdist/bundle.js
for validity.What is the expected behavior?
webpack
produces valid source mapssourcemap-validator
reads the sourcemap as valid (e.g., not throw an error).It looks like it might have something to do with some misaligned whitespace, but I'm really not sure.
Other relevant information:
webpack version: 4.23.1
Node.js version: 8.11.3
Operating System: macOS 10.14 (18A391)
Additional tools: sourcemap-validator@1.1.0, yarn@1.9.2
The text was updated successfully, but these errors were encountered: