Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging bundled javascript in Visual Studio 2015

I'm using WebPack with this simple config file to bundle my application.

var path = require('path');

module.exports = {
    debug: true,
    devtool: 'source-map',

    context: path.join(__dirname, 'js'),
    entry: './main.js',
    output: {
        path: path.join(__dirname, 'Built'),
        filename: '[name].bundle.js'
    }
};

This creates source mapping that I can easily use to debug my original javascript files in all popular browsers. However, setting breakpoint inside Visual Studio and running the project doesn't work, the breakpoints are disabled saying "No symbols have been loaded for this document". I'm debugging through IE11, where simple javascript can be debugged right away by Visual Studio, but after bundling this doesn't work anymore.

There is a sign that the sourcemapping works, because I get in console output Unsupported format of the sourcemap. The sourcemap generated the using config above looks like this

{"version":3,"sources":["webpack:///webpack/bootstrap 2919a5f916c286b8e21a","webpack:///./main.js","webpack:///./structure_editor/content.js","webpack:///./structure_editor/test_bundle.js"],"names":[],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;ACtCA;AACA;;AAEA;AACA;;AAEA;;AAEA;;AAEA;;;;;;;ACVA,8C;;;;;;ACAA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA,6B","file":"main.bundle.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 2919a5f916c286b8e21a\n **/","document.write(require(\"./structure_editor/content.js\"));\r\nvar TestBundle = require(\"./structure_editor/test_bundle.js\");\r\n\r\nvar test = new TestBundle();\r\ntest.testMe();\r\n\r\n//var StructureEditor = require(\"./structure_editor/structure_editor.js\");\r\n\r\n//var editor = new StructureEditor(0x00FF00);\r\n\r\n//editor.run();\r\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./main.js\n ** module id = 0\n ** module chunks = 0\n **/","module.exports = \"It works from content.js.\";\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./structure_editor/content.js\n ** module id = 1\n ** module chunks = 0\n **/","var TestBundle = function () {\r\n    \r\n}\r\n\r\nTestBundle.prototype.testMe = function() {\r\n    var a = 10;\r\n    var b = 12;\r\n    var c = a + b;\r\n    document.write(c);\r\n};\r\n\r\nmodule.exports = TestBundle;\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./structure_editor/test_bundle.js\n ** module id = 2\n ** module chunks = 0\n **/"],"sourceRoot":""}

So I can understand that this format might not be supported because of the webpack:// (although IE does understand it). However, if I knew how the proper format for VS looks like I might be able to tweak webpack to produce such format.

I'm asking for any ideas, tutorials,.. whatever to get this working.

like image 719
Raven Avatar asked Sep 07 '15 20:09

Raven


1 Answers

I don't have a full solution to the problem but I did narrow it down a bit.

The first thing to ensure is that the paths to both the source map and the original files are available to Visual Studio. I found that some of these request were getting the login page as a response. I granted anonymous access to the map and ts files. I did try using absolute, file system paths for the ts files which worked for Visual Studio but the browsers did not seem very happy about it.

From what I understand, the following is a shortcut for configuration of the SourceMapDevToolPlugin plugin. Going direct to the plugin will give you more control over the source maps that are generated.

devtool: 'source-map'

That would mean replacing the line above with something like the following.

config.plugins.push(new webpack.SourceMapDevToolPlugin({
    filename: '[file].map',
    moduleFilenameTemplate: "[resource-path]",
    fallbackModuleFilenameTemplate: "[resource-path]?[hash]"
}));

Note the [absolute-resource-path] macro which will result in file system paths that Visual Studio can use to access the files without loading them through the website. These are output in the sources property with backslashes escaped with a second backslash (C:\Projects\... for example). As I noted above though, this broke debugging in the browsers.

Also, the [resource-path] macro appears to be resolved during debugging as if it is relative to the folder that contains the source maps. This was not correct for my setup. I added a prefix and used something similar to the following to sort that out.

"../../app/[resource-path]"

The next issue was with the last segments for some of the lines within the mappings property (within the map files). This explanation of the source map format was very helpful here but pretty much, all you need to know is that semi colons separate lines and that each line can have multiple segments separated by commas. The segments can encode the following information.

  • Generated column
  • Original file this appeared in
  • Original line number
  • Original column
  • And if available original name.

I found that if I removed any short segments from the ends of lines that Visual Studio could process the map and I could set breakpoints etc. in the original files (Typescript files that were converted to javascript which was then bundled in my case).

To remove the short segments I used the following, manual process in Visual Studio for each of the map files. Note that I found it easier to interpret the files after I had formatted them by right clicking in the body of the document and selecting Format Document from the context menu.

  • Perform a replace operation with regular expressions enabled. Use the following expression in the find field and $1 as the value to replace.

    ,\s*[^\s\";]{1,3}?(;|\")

enter image description here

This will replace any segments that contain only 1, 2 or 3 characters at the end of lines.

It is possible that there will be problems setting breakpoints at the ends of lines but I have not been able to break it so far. I did notice though that when there were errors that caused execution to stop in the debugger that the lines did not always match up - maybe a result of this manipulation?

I also wrote a little console application that will perform this modification on all map files in a given folder. I run this automatically at the end of our build.

    static void Main(string[] args)
    {
        var sourceMapFolder = new DirectoryInfo(args[0]);

        foreach (var sourceMapFile in sourceMapFolder.EnumerateFiles("*.map"))
        {
            var sourceMapFilePath = sourceMapFile.FullName;

            var regex = new Regex(",\\s*[^\\s\\\";]{1,3}?(;|\\\")");

            var oldContent = File.ReadAllText(sourceMapFilePath);

            var newContent = regex.Replace(oldContent, "$1");

            File.WriteAllText(sourceMapFilePath, newContent);
        }
    }

I am unsure as to whether it is Webpack which should not be generating the segments or Visual Studio which should be handling them.

Update: I created a Bug on the Connect site for Visual Studio which covers this issue.

like image 183
Scott Munro Avatar answered Sep 30 '22 14:09

Scott Munro