tooling.report

feature

SVG

Do hashed SVG URLs change when subresources change?

Person in shorts with blue hair walking left

Introduction

Much like HTML, SVG documents can contain references to external resources like stylesheets, scripts and images. When hashing generated URLs to enable long-term caching, changes to the hashed URLs of these subresources must also cause the URL hash of the SVG to change.

The Test

In this test, the build tool processes one SVG image that references another SVG image, producing two SVGs with hashed URLs. The test is run multiple times, and in one of the runs the contents of the second image (bg.svg) are changed so that it generates a different hashed URL.

img.svg

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
  <image href="bg.svg" height="200" width="200" />
</svg>

bg.svg

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
  <circle cx="50" cy="50" r="50" />
</svg>

In this example, when bg.svg changes, its hashed URL bg.a1b2c.svg will change. Since the built SVG has to be updated with the new image URL, its hashed URL img.9z8y7.svg should also change.

If the content of bg.svg is changed between two builds, the hash of both output files must change compared to the previous build.

Conclusion

browserify

The plugin gulp-rev-all analyzes contained file references, rewrites them and calculates a final hash accordingly.

Note: gulp-rev is more popular, but would fail this test.

parcel

Parcel supports SVGs out of the box.

rollup

Plugin authors can use Rollup's emitFile method to add assets-of-assets into the graph. A placeholder hash (based on the content of the asset-of-asset) can be used to ensure changes to the asset-of-asset impacts the hash of the asset.

Extending this to assets-of-assets-of-assets would be a jump in complexity for the plugin author, although a simpler model is planned.

webpack

There are a few different ways to write a Webpack loader that bring new asset types into the dependency graph. The simplest is to convert the resource into a JavaScript module that exports it as a String with interleaved require() or import statements for the asset's dependencies. This often requires re-extracting the string back to its raw form in order to write it to disk, so it can be cleaner to instead use the loadModule() API to bring dependencies into the graph and evaluate them as part of processing the asset.

An undocumented API (module.buildInfo.assets) is required to get the generated module's hashed URL, so this is a 'partial' pass.

Issues