SharePoint Framework development tips: even more easily debug production version of your SharePoint Framework solution

Almost a year ago Waldek Mastykarz posted a great article on how to debug your SharePoint Framework solution in production, where all the code is minified and source maps are not available. While it works, there is a manual step of uploading source maps in google chrome's dev tools, which isn't very convenient. I extended Waldek's solution with fully automated implementation without any complexities. 

The problem

When you package your SharePoint Framework solution, spfx build pipeline minimizes and optimizes all your TypeScript code. It's not a problem for development, because in "serve" mode spfx build pipeline generates source maps for you. Source maps allow you to open original TypeScript source files in browser developer tools with full debugging experience. 

This is something not possible for "ship" or "production" mode of SharePoint Framework solution. When you run bundle and package-solution with "--ship" flag, spfx pipeline doesn't generate any source maps for you. Instead, you have minified and performance optimized javascript code without any notion of source maps. 

Here is your approximate production code revealed in Chrome dev tools (prettified):

Let's try to fix #1

What if we simply tell spfx to generate source maps and include them into the package?

It doesn't work because spfx ignores any source maps files and intentionally removes those files from the resulting package. 

Let's try to fix #2

Ok, what if we use inline source maps?

Not good, because it adds a lot to resulting file size and thus affects the performance of your solution. 

Should I include source maps in my sharepoint framework package?

It's debatable, my opinion that you should never include them. Source maps are solely development thing, and we use development things only for development, not for production, right? Another point is that with source maps on production everybody can take a look at your code. Maybe it's ok if your company's IT department is the author of a solution, but absolutely not good for ISV or other kinds of third party vendors. 

There is a solution to include source maps in the resulting bundle from a very smart guy Diogo Martins. His approach involves updating resulting .sppkg file with new files (source maps). You can use his method if it works for you. My opinion is that it's too complicated, thus I suggest another cleaner solution. 

The fix

Source maps

Spfx uses webpack for source map generation. Webpack has a lot of options for source maps generation and storage - check it out. You can store them in a separate file, generate inlined and many other options. If it's not enough you can use SourceMapDevToolPlugin for even more control over source maps generation. This plugin allows us to specify source maps to be loaded from a separate web server:

new webpack.SourceMapDevToolPlugin({
  append: '\n//# sourceMappingURL=https://example.com/sourcemap/[url]',
  filename: '[name].map'
});

This is exactly what we need! 

Updating SharePoint Framework build pipeline

You should update your gulpfile.js with below code (read explanation further):

const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
const webpack = require('webpack');

build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);

build.configureWebpack.mergeConfig({
  additionalConfiguration: (generatedConfiguration) => {

    generatedConfiguration.devtool = undefined;

    generatedConfiguration.plugins.push(new webpack.SourceMapDevToolPlugin({
      append: '\n//# sourceMappingURL=https://localhost:4321/dist/[url]',
      filename: '[name].map'
    }));

    for (var i = 0; i < generatedConfiguration.plugins.length; i++) {
      const plugin = generatedConfiguration.plugins[i];
      if (plugin instanceof webpack.optimize.UglifyJsPlugin) {
        plugin.options.sourceMap = true;
        break;
      }
    }

    return generatedConfiguration;
  }
});


build.initialize(gulp);
  1. Set devtool = undefined; This is required because we control source maps generation with SourceMapDevToolPlugin. Moreover, sometimes SourceMapDevToolPlugin is used by webpack under the hood for some devtool options. 
  2. Explicitly add SourceMapDevToolPlugin plugin to the list of webpack plugins. Here is the trick: we should use the custom web server to host source maps. We have such server when we run gulp serve. So let's use it to serve our source maps as well! The root path for the web server is our SharePoint solution root, so url https://localhost:4321/dist/ simply points to our <sharepoint solution root>/dist folder, where all the source map files are generated during serve command. 
  3. And the final step which is very important. --ship mode uses UglifyJsPlugin, we should enable source maps for that plugin in order source maps to be in the output: plugin.options.sourceMap = true;

How to finally debug production SharePoint Framework scripts

Now as you have your gulpfile.js updated, run: 

$ gulp bundle --ship
$ gulp package-solution --ship

A package will be created for you. This time every javascript file has source maps information: 

Upload the package to the tenant app catalog and deploy your web part (or extension). 

Then run

$ gulp serve --ship --nobrowser

This command spins up a local web server where all source maps are available by http protocol.

IMPORTANT: take a note at --ship parameter. You should use --ship with serve if you want to debug production files because in that case, webpack will generate the same maps used by production build (with UglifyJsPlugin). Otherwise, your breakpoints might not work. 

Finally, open a page with your web part, open dev tools, set breakpoints in source .ts files and debug like a boss! 

IMPORTANT: if your local version of a solution isn't the same as the production version, check out exactly the same version from source control and then run gulp serve --ship --nobrowser.

See how it works in action (source maps are not available when serve isn't running and available afterward):

As a result, we don't store source maps in our solution package yet have convenient debugging experience. 

In my opinion, this approach even can be included in the SPFx source code, because it doesn't break anything, and adds convenient production debug experience instead. 

 

Image credits: 

Background vector created by dooder - www.freepik.com

Business vector created by dooder - www.freepik.com