mzgoddard / hard-source-webpack-plugin

https://www.npmjs.com/package/hard-source-webpack-plugin
ISC License
2.7k stars 160 forks source link

archetype=Asset freezing bug, stores non-JSON string into cache #305

Open cspotcode opened 6 years ago

cspotcode commented 6 years ago

I'm seeing a bug using this plugin with webpack 3 --watch.

When freezing an asset (for example, from emitFile) the value to be frozen is a string, so a non-JSON string is stored into cache. When cache attempts to retrieve and parse this value as JSON, it throws an error.

Detailed breakdown of the flow through code:

store()... https://github.com/mzgoddard/hard-source-webpack-plugin/blob/master/index.js#L1561-L1565 ...calls freeze(): https://github.com/mzgoddard/hard-source-webpack-plugin/blob/master/index.js#L1519-L1529 ...which triggers '_hardSourceFreezeAsset', which is tapped here: https://github.com/mzgoddard/hard-source-webpack-plugin/blob/master/lib/hard-asset-plugin.js#L9 It calls asset.source() which returns a string according to the docs: https://github.com/webpack/webpack-sources#source-1

Back in store(), the result of calling freeze() (a string) is passed to cache.set()

This cached value is a string of the asset's source code.

Later, when we try to retrieve that cached value via cache.get(), it attempts to JSON.parse the string, but the string is not valid JSON, so I see an error: https://github.com/mzgoddard/hard-source-webpack-plugin/blob/master/index.js#L1409-L1410

I think this a logic error but I'm not sure what the intended behavior is because the codebase doesn't have any sort of type annotations. Can someone help me debug and write a PR to fix this behavior?

cspotcode commented 6 years ago

I'm using this as a workaround, but I'm not 100% sure it's working:

class HardSourceFixBug {
    apply(compiler) {
        compiler.plugin('_hardSourceAfterFreezeAsset', (frozen, item, extra) => {
            if(typeof frozen === 'string') {
                console.log('WRAPPING string in FROZEN WRAPPER');
                return {___frozenString: true, ___frozenStringValue: frozen};
            }
            return frozen;
        });
        compiler.plugin('_hardSourceBeforeThawAsset', (thawed, asset, extra) => {
            if (!thawed) {
                thawed = asset;
                if(thawed && thawed.___frozenString) {
                    thawed = thawed.___frozenStringValue;
                }
                if (thawed.type === 'buffer') {
                    thawed = new Buffer(thawed);
                }
                if (!(thawed instanceof RawSource)) {
                    thawed = new RawSource(thawed);
                }
            }

            return thawed;
        });
    }
}