Skip to content

Commit

Permalink
Convert the service worker to work with Vite
Browse files Browse the repository at this point in the history
  • Loading branch information
renchap committed Apr 23, 2024
1 parent c822df0 commit 8e7cb56
Show file tree
Hide file tree
Showing 10 changed files with 2,215 additions and 168 deletions.
9 changes: 6 additions & 3 deletions app/javascript/mastodon/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import * as perf from 'mastodon/performance';
import ready from 'mastodon/ready';
import { store } from 'mastodon/store';

import { isProduction } from './utils/environment';

import { isDevelopment, isProduction } from './utils/environment';

const enableServiceWorker = isProduction();

/**
* @returns {Promise<void>}
Expand All @@ -23,9 +26,9 @@ function main() {
root.render(<Mastodon {...props} />);
store.dispatch(setupBrowserNotifications());

if (isProduction() && me && 'serviceWorker' in navigator) {
if (enableServiceWorker && me && 'serviceWorker' in navigator) {
const { Workbox } = await import('workbox-window');
const wb = new Workbox('/sw.js');
const wb = new Workbox(isDevelopment() ? '/vite-dev/dev-sw.js?dev-sw' : '/sw.js', { type: 'module', scope: '/' });
/** @type {ServiceWorkerRegistration} */
let registration;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute } from 'workbox-precaching';
import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';

Expand All @@ -15,10 +15,11 @@ function fetchRoot() {
return fetch('/', { credentials: 'include', redirect: 'manual' });
}

cleanupOutdatedCaches();
precacheAndRoute(self.__WB_MANIFEST);

registerRoute(
/locale_.*\.js$/,
/intl\/.*\.js$/,
new CacheFirst({
cacheName: `${CACHE_NAME_PREFIX}locales`,
plugins: [
Expand Down Expand Up @@ -46,7 +47,7 @@ registerRoute(
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: `m${CACHE_NAME_PREFIX}media`,
cacheName: `${CACHE_NAME_PREFIX}media`,
plugins: [
new ExpirationPlugin({
maxAgeSeconds: 7 * 24 * 60 * 60, // 1 week
Expand All @@ -56,8 +57,8 @@ registerRoute(
}),
);

// Cause a new version of a registered Service Worker to replace an existing one
// that is already installed, and replace the currently active worker on open pages.
// // Cause a new version of a registered Service Worker to replace an existing one
// // that is already installed, and replace the currently active worker on open pages.
self.addEventListener('install', function(event) {
event.waitUntil(Promise.all([openWebCache(), fetchRoot()]).then(([cache, root]) => cache.put('/', root)));
});
Expand Down
37 changes: 0 additions & 37 deletions app/javascript/mastodon/service_worker/web_push_locales.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { IntlMessageFormat } from 'intl-messageformat';

import { unescape } from 'lodash';

import locales from './web_push_locales';
// see config/vite/plugins/sw-locales
// it needs to be updated when new locale keys are used in this file
// eslint-disable-next-line import/no-unresolved
import locales from "virtual:mastodon-sw-locales";

const MAX_NOTIFICATIONS = 5;
const GROUP_TAG = 'tag';
Expand Down
1 change: 1 addition & 0 deletions app/javascript/types/pwa.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite-plugin-pwa/client" />
79 changes: 79 additions & 0 deletions config/vite/plugins/sw-locales/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* This plugin provides the `virtual:mastodon-sw-locales` import
which exports translations for every locales, but only with the
keys defined below.
This is used by the notifications code in the service-worker, to
provide localised texts without having to load all the translations
*/

import fs from 'node:fs';
import path from 'node:path';

import type { Plugin, ResolvedConfig } from 'vite';

const KEEP_KEYS = [
'notification.favourite',
'notification.follow',
'notification.follow_request',
'notification.mention',
'notification.reblog',
'notification.poll',
'notification.status',
'notification.update',
'notification.admin.sign_up',
'status.show_more',
'status.reblog',
'status.favourite',
'notifications.group',
];

export function MastodonServiceWorkerLocales(): Plugin {
const virtualModuleId = 'virtual:mastodon-sw-locales';
const resolvedVirtualModuleId = '\0' + virtualModuleId;

let config: ResolvedConfig;

return {
name: 'mastodon-sw-locales',
configResolved(resolvedConfig) {
config = resolvedConfig;
},
resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId;
}

return undefined;
},
load(id) {
if (id === resolvedVirtualModuleId) {
const filteredLocales: Record<string, Record<string, string>> = {};
const localesPath = path.resolve(config.root, 'mastodon/locales');

const filenames = fs.readdirSync(localesPath);

filenames
.filter((filename) => filename.match(/[a-zA-Z-]+\.json$/))
.forEach((filename) => {
const content = fs.readFileSync(
path.resolve(localesPath, filename),
'utf-8',
);
const full = JSON.parse(content) as Record<string, string>;
const locale = filename.split('.')[0];

const filteredLocale: Record<string, string> = {};

Object.entries(full).forEach(([key, value]) => {
if (KEEP_KEYS.includes(key)) filteredLocale[key] = value;
});

filteredLocales[locale] = filteredLocale;
});

return `const locales = ${JSON.stringify(filteredLocales)}; \n export default locales;`;
}

return undefined;
},
};
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
"stylelint": "^16.0.2",
"stylelint-config-standard-scss": "^13.0.0",
"typescript": "^5.0.4",
"vite-plugin-pwa": "^0.19.8",
"vitest": "^1.5.0",
"vitest-github-actions-reporter": "^0.11.1"
},
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"vite.config.mts",
"app/javascript/mastodon",
"app/javascript/entrypoints",
"app/javascript/types"
"app/javascript/types",
"config/vite/plugins"
]
}
38 changes: 38 additions & 0 deletions vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import { optimizeLodashImports } from '@optimize-lodash/rollup-plugin';
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import { analyzer } from 'vite-bundle-analyzer';
import { VitePWA } from 'vite-plugin-pwa';
import RailsPlugin from 'vite-plugin-rails';
import svgr from 'vite-plugin-svgr';
import { configDefaults } from 'vitest/config';
import GithubActionsReporter from 'vitest-github-actions-reporter';

import { MastodonServiceWorkerLocales } from './config/vite/plugins/sw-locales';

const sourceCodeDir = 'app/javascript';
const items = fs.readdirSync(sourceCodeDir);
const directories = items.filter((item) =>
Expand Down Expand Up @@ -80,11 +83,46 @@ export default defineConfig({
],
},
}),
MastodonServiceWorkerLocales(),
VitePWA({
mode: 'development',
strategies: 'injectManifest',
srcDir: 'mastodon/service_worker',
filename: 'sw.js',
manifest: false,
injectRegister: null,
injectManifest: {
buildPlugins: {
vite: [
// Provide a virtual import with only the locales used in the ServiceWorker
MastodonServiceWorkerLocales(),
],
},
globIgnores: [
// Do not preload those files
'intl/*.js',
'extra_polyfills-*.js',
'polyfill-force-*.js',
'assets/mailer-*.{js,css}',
],
maximumFileSizeToCacheInBytes: 2 * 1_024 * 1_024, // 2 MiB
},
devOptions: {
enabled: true,
type: 'module',
},
}),
svgr(),
// @ts-expect-error the types for the plugin are not up-to-date
optimizeLodashImports(),
!!process.env.ANALYZE_BUNDLE_SIZE && analyzer({ analyzerMode: 'static' }),
],
server: {
headers: {
// This is needed in dev environment because we load the worker from `/dev-sw/dev-sw.js`, but it needs to be scoped to the whole domain
'Service-Worker-Allowed': '/',
},
},
test: {
environment: 'jsdom',
include: [
Expand Down

0 comments on commit 8e7cb56

Please sign in to comment.