Skip to content
This repository has been archived by the owner on Nov 4, 2019. It is now read-only.

This repository demonstrates a common problem where webpack loaders are responsible for broken source maps.

Notifications You must be signed in to change notification settings

jhnns/webpack-broken-source-maps

Repository files navigation

How to break source maps with webpack loaders

This repository demonstrates a common problem where webpack loaders are responsible for broken source maps. You will notice broken source maps by jumping break points when you're trying to debug your application.

The problem

A simple implementation of a webpack loader could generate additional JavaScript like this:

function brokenLoader(content, sourceMap) {
    // Don't do this since it makes the sourceMap information useless :(
    const newContent = "// this is some content which will offset the source map by one line\n" + content;
    this.callback(null, newContent, sourceMap);
}

The problem is that the mappings from the received sourceMap will not match the generated content anymore. You can see the problem in this example. Scroll down until you see console.log("Hello world"); and hover over it. You'll see that the mapping between the generated code and the original source code is not correct.

The solution

Use the source-map module if you need to pre- or append strings. Unfortunately, the API is not straight-forward to use. This is the boilerplate code to fix the problem from above:

const prefix = "// this is some content which will offset the source map by one line\n";

// Either create a SourceNode instance from scratch or based on an input sourceMap
async function createSourceNode(resourcePath, content, sourceMap) {
    if (sourceMap === undefined) {
        const node = new SourceNode(1, 0, resourcePath, content);

        node.setSourceContent(resourcePath, content);

        return node;
    }

    return SourceMapConsumer.with(sourceMap, null, consumer =>
        SourceNode.fromStringWithSourceMap(content, consumer)
    );
}

async function fixedLoader(content, sourceMap) {
    if (!this.sourceMap) {
        // If we don't care about source maps, we can just do whatever we want.
        return prefix + content;
    }

    this.async();

    const node = await createSourceNode(this.resourcePath, content, sourceMap);

    node.prepend(
        // In our case, the generated loader code cannot be mapped to the original source code
        // so let's prepend some code without any mappings to original line and column numbers.
        new SourceNode(
            null, // null = no original line number
            null, // null = no original column number
            null, // null = no original source file
            prefix
        )
    );

    const { code, map: mapGenerator } = node.toStringWithSourceMap();
    // JSON.parse(mapGenerator.toString()) is kind of stupid but that's
    // the way how the source-map module works. Maybe since maps are generated by WASM?
    const map = JSON.parse(mapGenerator.toString());

    this.callback(null, code, map);
}

Take a look at the new output. The generated mapping is correct now.

Testing source maps

source-map-visualization is an excellent tool to debug and validate the generated source maps. The source-map module also provides a way to query row and column mappings which you should do in your loader test suite to verify that the source maps are correct.

About

This repository demonstrates a common problem where webpack loaders are responsible for broken source maps.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published