Serving

# Caching & ETags

Fold the import map into your ETags, and keep the map fresh while you edit JavaScript in development.

## A digest of the import map in your ETag

If you use ETags generated by Rails helpers like `stale?` or `fresh_when`, the digest of the import map has to be part of the calculation. Otherwise the application keeps answering `304 Not Modified` after your JavaScript changed. `stale_when_importmap_changes` adds the digest to the ETag for HTML requests:

```ruby
class ApplicationController < ActionController::Base
  stale_when_importmap_changes
end
```

Under the hood that is `Rails.application.importmap.digest(resolver: helpers)`, a SHA1 of the rendered import map JSON, which you can use directly in an `etag { … }` block if you'd rather scope it yourself.

## Sweeping the cache in development and test

The import map is cached once rendered. In development, and in test when class reloading is on, a file watcher on `app/javascript` and `vendor/javascript` clears that cache before each request when a file changed, so a new controller shows up in the map without a restart. That is `config.importmap.sweep_cache`, on by default in those environments; the watcher is only installed when classes are reloadable, so a test environment with `enable_reloading = false` (the Rails default) skips it.

An engine, or an app with JavaScript elsewhere, adds its directories to the watch list. The watcher reads the list in the `importmap.cache_sweeper` initializer, so an engine's initializer has to run before it:

```ruby
initializer "my-engine.importmap", before: "importmap.cache_sweeper" do |app|
  app.config.importmap.cache_sweepers << Engine.root.join("app/assets/javascripts")
end
```

`config/importmap.rb` itself is reloaded by a separate reloader whenever any file in `config.importmap.paths` changes; removing a pin still needs a restart for it to disappear from the rendered map and the preloads.

## Legacy browsers such as Safari on iOS 15

For a browser without native import map support, load [es-module-shims](https://github.com/guybedford/es-module-shims) before the import map tags:

```ruby
<script async src="https://ga.jspm.io/npm:es-module-shims@1.8.2/dist/es-module-shims.js" data-turbo-track="reload"></script>
<%= javascript_importmap_tags %>
```