1

I'm using Webpack to bundle source code and assets for a game. I also use the CompressionPlugin() to make static gzip files available so that my web server can send the precompressed files when appropriate. Some of the game assets are large so I have a loading experience up front that shows a progress bar while the assets are fetched.

Unfortunately a problem arises on Chome during loading when receiving a gzip response for an XMLHttpRequest in that the onprogress total is always 0. There are some imperfect workarounds for this such as this solution but they're not entirely appropriate for my case.

Instead I'd like to inject the compressed & decompressed file sizes of specific bundled assets into the html or javascript so that they're immediately accessible to the loading javascript code. Injecting something as follows would be perfect:

<script>
const assetSizes = {
    "game.25317abd3eb6cf0fb0f1.wasm": {uncompressed: 8192, compressed: 1024},
    "game.25317abd3eb6cf0fb0f1.data": {uncompressed: 8192, compressed: 1024}
};
</script>

I'm somewhat new to webpack so I'm not entirely sure how to approach this. I've considered using the WebpackManifestPlugin and implementing a custom generate option function. This would allow me to control the output of the generated manifest.json but it's still not clear to me if this the right thing to do or how I'd go about subsequently injecting this files contents ahead of my own loading javascript code.

Perhaps there is a better approach that would be more appropriate?


Update: I've been trying to progress this further and it feels like a custom Webpack plugin might be the right direction to go. If I tap into the afterEmit compiler hook it seems I have access to the filesizes I need and I can construct an appropriate dictionary:

class InjectFileSizePlugin {
    apply(compiler) {
        compiler.hooks.afterEmit.tap(
            "InjectFileSizePlugin",
            (compilation) => {
                const fileSizes = {};
                for (const [assetPath, assetInfo] of compilation.assetsInfo) {
                    if (assetInfo.related) {
                        fileSizes[assetPath] = {
                            uncompressed: assetInfo.size,
                            compressed: -1
                        };
                        if (assetInfo.related.gzipped) {
                            const gzippedAssetInfo = compilation.assetsInfo.get(
                                assetInfo.related.gzipped
                            );
                            if (gzippedAssetInfo) {
                                fileSizes[assetPath].compressed =
                                    gzippedAssetInfo.size;
                            }
                        }
                    }
                }
                console.log(fileSizes); // <-- output is as I'd like, how to inject it now?
            }
        );
    }
}

What's not clear though is how I can now go about injecting this fileSizes data into the bundle as afterEmit is called very late in the compilation stage after the bundle javascript has been emitted. There is an additionalPass compiler hook but I currently can't figure out how it works.

dbotha
  • 1,501
  • 4
  • 20
  • 38

0 Answers0