{"id":13416418,"url":"https://github.com/rossta/serviceworker-rails","last_synced_at":"2025-05-14T02:09:49.299Z","repository":{"id":7944087,"uuid":"56937166","full_name":"rossta/serviceworker-rails","owner":"rossta","description":"Use Service Worker with the Rails asset pipeline","archived":false,"fork":false,"pushed_at":"2024-10-28T20:19:23.000Z","size":691,"stargazers_count":587,"open_issues_count":19,"forks_count":50,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-05-13T20:07:59.204Z","etag":null,"topics":["javascript","rails","rails-asset-pipeline","service-worker","sprockets"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rossta.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-04-23T19:31:02.000Z","updated_at":"2025-05-11T21:38:12.000Z","dependencies_parsed_at":"2024-06-18T14:02:29.871Z","dependency_job_id":"9c5abe14-38da-4364-a80c-33fcef3052ae","html_url":"https://github.com/rossta/serviceworker-rails","commit_stats":{"total_commits":189,"total_committers":14,"mean_commits":13.5,"dds":0.1164021164021164,"last_synced_commit":"a81aa1f138716e4eda8c1241c8b8152c1b1bda9c"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rossta%2Fserviceworker-rails","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rossta%2Fserviceworker-rails/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rossta%2Fserviceworker-rails/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rossta%2Fserviceworker-rails/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rossta","download_url":"https://codeload.github.com/rossta/serviceworker-rails/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254029499,"owners_count":22002396,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","rails","rails-asset-pipeline","service-worker","sprockets"],"created_at":"2024-07-30T21:00:58.535Z","updated_at":"2025-05-14T02:09:44.276Z","avatar_url":"https://github.com/rossta.png","language":"Ruby","funding_links":[],"categories":["Ruby","Tools"],"sub_categories":["Webpack and Framework Plugins"],"readme":"# ServiceWorker::Rails\n\n[![Build Status](https://travis-ci.org/rossta/serviceworker-rails.svg?branch=master)](https://travis-ci.org/rossta/serviceworker-rails)\n[![Code Climate](https://codeclimate.com/github/rossta/serviceworker-rails/badges/gpa.svg)](https://codeclimate.com/github/rossta/serviceworker-rails)\n\nTurn your Rails app into a Progressive Web App. Use [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) with the Rails [asset pipeline](https://github.com/rails/sprockets-rails) or [Webpacker](https://github.com/rails/webpacker)\n\n## Why?\n\nThe Rails asset pipeline makes a number of assumptions about what's best for deploying JavaScript, including asset digest fingerprints and long-lived cache headers - mostly to increase \"cacheability\". Rails also assumes a single parent directory, `/public/assets`, to make it easier to look up the file path for a given asset.\n\nService worker assets must play by different rules. Consider these behaviors:\n\n* Service workers may only be active from within the scope from which they are\nserved. So if you try to register a service worker from a Rails asset pipeline\npath, like `/assets/serviceworker-abcd1234.js`, it will only be able to interact\nwith requests and responses within `/assets/`\u003cem\u003e**\u003c/em\u003e. This is not what we want.\n\n* [MDN states](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API#Download_install_and_activate) browsers check for updated service worker scripts in the background every 24 hours (possibly less). Rails developers wouldn't be able to take advantage of this feature since the fingerprint strategy means assets at a given url are immutable. Beside fingerprintings, the `Cache-Control` headers used for static files served from Rails also work against browser's treatment of service workers.\n\nWe want Sprockets or Webpacker to compile service worker JavaScript from ES6/7, CoffeeScript, ERB, etc. but must remove the caching and scoping mechanisms offered by Rails defaults. This is where `serviceworker-rails` comes in.\n\n*Check out the [blog post](https://rossta.net/blog/service-worker-on-rails.html)\nfor more background.*\n\n### Demo\n\nSee various examples of using Service Workers in the demo Rails app, [Service Worker Rails Sandbox](https://serviceworker-rails.herokuapp.com/). The [source code](https://github.com/rossta/serviceworker-rails-sandbox) is also on GitHub.\n\n## Features\n\n* Maps service worker endpoints to Rails assets\n* Adds appropriate response headers to service workers\n* Renders compiled source in production and development\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'serviceworker-rails'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install serviceworker-rails\n\nTo set up your Rails project for use with a Service Worker, you either use the\nRails generator and edit the generated files as needed, or you can follow the\nmanual installation steps.\n\n### Automated setup\n\nAfter bundling the gem in your Rails project, run the generator from the root of\nyour Rails project.\n\n```\n$ rails g serviceworker:install\n```\n\nThe generator will create the following files:\n\n* `config/initializers/serviceworker.rb` - for configuring your Rails app\n* `app/assets/javascripts/serviceworker.js.erb` - a blank Service Worker\n  script with some example strategies\n* `app/assets/javascripts/serviceworker-companion.js` - a snippet of JavaScript\n  necessary to register your Service Worker in the browser\n* `app/assets/javascripts/manifest.json.erb` - a starter web app manifest\n  pointing to some default app icons provided by the gem\n* `public/offline.html` - a starter offline page\n\nIt will also make the following modifications to existing files:\n\n* Adds a sprockets directive to `application.js` to require\n  `serviceworker-companion.js`\n* Adds `serviceworker.js` and `manifest.json` to the list of compiled assets in\n  `config/initializers/assets.rb`\n* Injects tags into the `head` of `app/views/layouts/application.html.erb` for\n  linking to the web app manifest\n\n**NOTE** Given that Service Worker operates in a separate browser thread, outside the context of your web pages, you don't want to include `serviceworker.js` script in your `application.js`. So if you have a line like `require_tree .` in your `application.js` file, you'll either need to move your `serviceworker.js` to another location or replace `require_tree` with something more explicit.\n\nTo learn more about each of the changes or to perform the set up yourself, check\nout the manual setup section below.\n\n### Manual setup\n\nLet's add a `ServiceWorker` to cache some of your JavaScript and CSS assets. We'll assume you already have a Rails application using the asset pipeline built on Sprockets.\n\n#### Add a service worker script\n\nCreate a JavaScript file called `app/assets/javascripts/serviceworker.js.erb`:\n\n```javascript\n// app/assets/javascripts/serviceworker.js.erb\nconsole.log('[Service Worker] Hello world!');\n\nvar CACHE_NAME = 'v1-cached-assets'\n\nfunction onInstall(event) {\n  event.waitUntil(\n    caches.open(CACHE_NAME).then(function prefill(cache) {\n      return cache.addAll([\n        '\u003c%= asset_path \"application.js\" %\u003e',\n        '\u003c%= asset_path \"application.css\" %\u003e',\n        '/offline.html',\n        // you get the idea ...\n      ]);\n    })\n  );\n}\n\nfunction onActivate(event) {\n  console.log('[Serviceworker]', \"Activating!\", event);\n  event.waitUntil(\n    caches.keys().then(function(cacheNames) {\n      return Promise.all(\n        cacheNames.filter(function(cacheName) {\n          // Return true if you want to remove this cache,\n          // but remember that caches are shared across\n          // the whole origin\n           return cacheName.indexOf('v1') !== 0;\n        }).map(function(cacheName) {\n          return caches.delete(cacheName);\n        })\n      );\n    })\n  );\n}\n\nself.addEventListener('install', onInstall)\nself.addEventListener('activate', onActivate)\n```\n\nFor use in production, instruct Sprockets to precompile service worker scripts separately from `application.js`, as in the following example:\n\n#### Register the service worker\n\nYou'll need to register the service worker with a companion script in your main page JavaScript, like `application.js`. You can use the following:\n\n```javascript\n// app/assets/javascripts/serviceworker-companion.js\n\nif (navigator.serviceWorker) {\n  navigator.serviceWorker.register('/serviceworker.js', { scope: './' })\n    .then(function(reg) {\n      console.log('[Page] Service worker registered!');\n    });\n}\n\n// app/assets/javascripts/application.js\n\n// ...\n//= require serviceworker-companion\n```\n\n#### Add a manifest\n\nYou may also want to create a `manifest.json` file to make your web app installable.\n\n```javascript\n// app/assets/javascripts/manifest.json\n{\n  \"name\": \"My Progressive Rails App\",\n  \"short_name\": \"Progressive\",\n  \"start_url\": \"/\"\n}\n```\n\nYou'd then link to your manifest from the application layout:\n\n```html\n\u003clink rel=\"manifest\" href=\"/manifest.json\" /\u003e\n```\n\n#### Configure the middleware\n\nNext, add a new initializer as show below to instruct the `serviceworker-rails`\nmiddleware how to route requests for assets by canonical url.\n\n```ruby\n# config/initializers/serviceworker.rb\n\nRails.application.configure do\n  config.serviceworker.routes.draw do\n    match \"/serviceworker.js\"\n    match \"/manifest.json\"\n  end\nend\n```\n\n#### Precompile the assets\n\n```ruby\n# config/initializers/assets.rb\n\nRails.application.configure do\n  config.assets.precompile += %w[serviceworker.js manifest.json]\nend\n```\n\n#### Test the setup\n\nAt this point, restart your Rails app and reload a page in your app in Chrome or Firefox. Using dev tools, you should be able to determine.\n\n1. The page requests a service worker at `/serviceworker.js`\n2. The Rails app responds to the request by compiling and rendering the file in `app/assets/javascripts/serviceworker.js.erb`.\n3. The console displays messages from the page and the service worker\n4. The application JavaScript and CSS assets are added to the browser's request/response [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache).\n\n#### Using the cache\n\nSo far so good? At this point, all we've done is pre-fetched assets and added them to the cache, but we're not doing anything with them yet.\n\nNow, we can use the service worker to intercept requests and either serve them from the cache if they exist there or fallback to the network response otherwise. In most cases, we can expect responses coming from the local cache to be much faster than those coming from the network.\n\n```javascript\n// app/assets/javascripts/serviceworker.js.erb\n\nfunction onFetch(event) {\n  // Fetch from network, fallback to cached content, then offline.html for same-origin GET requests\n  var request = event.request;\n\n  if (!request.url.match(/^https?:\\/\\/example.com/) ) { return; }\n  if (request.method !== 'GET') { return; }\n\n  event.respondWith(\n    fetch(request)                                        // first, the network\n      .catch(function fallback() {\n         caches.match(request).then(function(response) {  // then, the cache\n           response || caches.match(\"/offline.html\");     // then, /offline cache\n         })\n       })\n  );\n\n  // See https://jakearchibald.com/2014/offline-cookbook/#on-network-response for more examples\n}\n\nself.addEventListener('fetch', onFetch);\n```\n\n## Configuration\n\nWhen `serviceworker-rails` is required in your Gemfile, it will insert a middleware into the Rails\nmiddleware stack. You'll want to configure it by mapping serviceworker routes to\nSprockets JavaScript assets in an initializer, like the example below.\n\n```ruby\n# config/initializers/serviceworker.rb\n\nRails.application.configure do\n  config.serviceworker.routes.draw do\n    # maps to asset named 'serviceworker.js' implicitly\n    match \"/serviceworker.js\"\n\n    # map to a named asset explicitly\n    match \"/proxied-serviceworker.js\" =\u003e \"nested/asset/serviceworker.js\"\n    match \"/nested/serviceworker.js\" =\u003e \"another/serviceworker.js\"\n\n    # capture named path segments and interpolate to asset name\n    match \"/captures/*segments/serviceworker.js\" =\u003e \"%{segments}/serviceworker.js\"\n\n    # capture named parameter and interpolate to asset name\n    match \"/parameter/:id/serviceworker.js\" =\u003e \"project/%{id}/serviceworker.js\"\n\n    # insert custom headers\n    match \"/header-serviceworker.js\" =\u003e \"another/serviceworker.js\",\n      headers: { \"X-Resource-Header\" =\u003e \"A resource\" }\n\n    # maps to serviceworker \"pack\" compiled by Webpacker\n    match \"/webpack-serviceworker.js\" =\u003e \"serviceworker.js\", pack: true\n\n    # anonymous glob exposes `paths` variable for interpolation\n    match \"/*/serviceworker.js\" =\u003e \"%{paths}/serviceworker.js\"\n  end\nend\n```\n\n`Serviceworker::Rails` will insert a `Cache-Control` header to instruct browsers\nnot to cache your serviceworkers by default. You can customize the headers for all service worker routes if you'd like,\nsuch as adding the experimental [`Service-Worker-Allowed`](https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#service-worker-allowed) header to set the allowed scope.\n\n```ruby\nconfig.serviceworker.headers[\"Service-Worker-Allowed\"] = \"/\"\nconfig.serviceworker.headers[\"X-Custom-Header\"] = \"foobar\"\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/rossta/serviceworker-rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frossta%2Fserviceworker-rails","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frossta%2Fserviceworker-rails","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frossta%2Fserviceworker-rails/lists"}