Facing problem
I want to convert a web application created with Rails 7 that uses importmap into a PWA However, I'm having trouble registering the service-worker using importmap. I'd appreciate it if you could give me some advice.
Status quo
At first, I tried to use this gem, but it didn't work in the importmap environment, so I referred to this site, but it didn't work.
The result of lighthouse check is as below.
Development environment
ruby 3.1.2
rails 7.0.3
importmap-rails 1.1.2
tailwindcss-rails 2.0.10
Related source code
config/route.rb
get '/service-worker.js', to: 'service_worker#service_worker'
get '/offline.html', to: 'service_worker#offline'
./app/controllers
├── service_worker_controller.rb
class ServiceWorkerController < ApplicationController
protect_from_forgery except: :service_worker
skip_before_action :require_login
def service_worker; end
def offline; end
end
./app/views/service_worker
├── offline.html.erb
└── service-worker.js
const VERSION = 'v1';
const NAME = 'app_name-';
const CACHE_NAME = NAME + VERSION;
const urlsToCache = [
"./offline.html"
];
self.addEventListener('install', function (event) {
event.waitUntil(
caches.open(CACHE_NAME).then(function (cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function (event)
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin')
return;
event.respondWith(
cache.match(event.request).then(function (response) {
if (response) {
return response;
}
return fetch(event.request);
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(
cache.keys().then(keys => Promise.all(
keys.map(key => {
if (!CACHE_NAME.includes(key)) {
return cache.delete(key);
}
})
)).then(() => {
console.log(CACHE_NAME + "activated");
})
);
});
/app/javascript/controllers
└── service-worker_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect () {
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register("/service-worker.js", {scope: "./" }).then(function (registration) {
console.log("ServiceWorker registration successful with scope: ", registration.scope);
}, function (err) {
console.log("ServiceWorker registration failed: ", err);
});
});
}
}
}
app/views/layouts/
└── application.html.erb
<!DOCTYPE html>
<html lang="ja">
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<title><%= page_title(yield(:title)) %></title>
<%= render 'application/favicon' %>
<%= display_meta_tags(default_meta_tags) %>
</head>
<body class="pb-420 box-border break-words">
<main>
<div data-controller="service-worker">
<%= render 'shared/navbar' %>
<%= render 'shared/flash' %>
<div class="w-full" data-controller="service_worker">
<%= yield %>
<%= render 'shared/footer' %>
</div>
</div>
</main>
</body>
</html>
Error message
when I reload the page, The following error appears on the console of chrome DV tool.
The script does not have a MIME type.
Also, the following message appears on the terminal.
Started GET "/service-worker.js" for ::1 at xxxxxxxxxxxx
18:49:40 web.1 | Processing by ServiceWorkerController#service_worker as */*
18:49:40 web.1 | No template found for ServiceWorkerController#service_worker, rendering head :no_content
18:49:40 web.1 | Completed 204 No Content in 3ms (ActiveRecord: 0.0ms | Allocations: 1278)
Please let me know if you find anything strange.
