From ec05b20b06dfc25e939b989ceb577a5a9b0b7f04 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 2 Jun 2026 23:16:03 +0300 Subject: [PATCH 1/2] stepping back --- build/sw_plugin.js | 193 +++++++++++++++++++++++++++++++++++++++------ vite.config.js | 12 +-- 2 files changed, 176 insertions(+), 29 deletions(-) diff --git a/build/sw_plugin.js b/build/sw_plugin.js index ee952d229..39cc31405 100644 --- a/build/sw_plugin.js +++ b/build/sw_plugin.js @@ -3,7 +3,6 @@ import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import * as esbuild from 'esbuild' import { build } from 'vite' -import { exactRegex } from '@rolldown/pluginutils' import { generateServiceWorkerMessages, @@ -16,15 +15,22 @@ const getSWMessagesAsText = async () => { } const projectRoot = dirname(dirname(fileURLToPath(import.meta.url))) +const swEnvName = 'virtual:pleroma-fe/service_worker_env' +const swEnvNameResolved = '\0' + swEnvName const getDevSwEnv = () => `self.serviceWorkerOption = { assets: [] };` const getProdSwEnv = ({ assets }) => `self.serviceWorkerOption = { assets: ${JSON.stringify(assets)} };` -export const devSwPlugin = ({ swSrc, swDest }) => { +export const devSwPlugin = ({ swSrc, swDest, transformSW, alias }) => { const swFullSrc = resolve(projectRoot, swSrc) + const esbuildAlias = {} + Object.entries(alias).forEach(([source, dest]) => { + esbuildAlias[source] = dest.startsWith('/') ? projectRoot + dest : dest + }) return { - name: 'dev-sw-plugin', apply: 'serve', + name: 'dev-sw-plugin', + apply: 'serve', configResolved() { /* no-op */ }, @@ -32,35 +38,174 @@ export const devSwPlugin = ({ swSrc, swDest }) => { const name = id.startsWith('/') ? id.slice(1) : id if (name === swDest) { return swFullSrc + } else if (name === swEnvName) { + return swEnvNameResolved } return null }, async load(id) { if (id === swFullSrc) { return readFile(swFullSrc, 'utf-8') + } else if (id === swEnvNameResolved) { + return getDevSwEnv() + } + return null + }, + /** + * vite does not bundle the service worker + * during dev, and firefox does not support ESM as service worker + * https://bugzilla.mozilla.org/show_bug.cgi?id=1360870 + */ + async transform(code, id) { + if (id === swFullSrc && transformSW) { + const res = await esbuild.build({ + entryPoints: [swSrc], + bundle: true, + write: false, + outfile: 'sw-pleroma.js', + alias: esbuildAlias, + plugins: [ + { + name: 'vite-like-root-resolve', + setup(b) { + b.onResolve({ filter: new RegExp(/^\//) }, (args) => ({ + path: resolve(projectRoot, args.path.slice(1)), + })) + }, + }, + { + name: 'sw-messages', + setup(b) { + b.onResolve( + { filter: new RegExp('^' + swMessagesName + '$') }, + (args) => ({ + path: args.path, + namespace: 'sw-messages', + }), + ) + b.onLoad( + { filter: /.*/, namespace: 'sw-messages' }, + async () => ({ + contents: await getSWMessagesAsText(), + }), + ) + }, + }, + { + name: 'sw-env', + setup(b) { + b.onResolve( + { filter: new RegExp('^' + swEnvName + '$') }, + (args) => ({ + path: args.path, + namespace: 'sw-env', + }), + ) + b.onLoad({ filter: /.*/, namespace: 'sw-env' }, () => ({ + contents: getDevSwEnv(), + })) + }, + }, + ], + }) + const text = res.outputFiles[0].text + return text + } + }, + } +} + +// Idea taken from +// https://github.com/vite-pwa/vite-plugin-pwa/blob/main/src/plugins/build.ts +// rollup does not support compiling to iife if we want to code-split; +// however, we must compile the service worker to iife because of browser support. +// Run another vite build just for the service worker targeting iife at +// the end of the build. +export const buildSwPlugin = ({ swSrc, swDest }) => { + let config + return { + name: 'build-sw-plugin', + enforce: 'post', + apply: 'build', + configResolved(resolvedConfig) { + config = { + define: resolvedConfig.define, + resolve: resolvedConfig.resolve, + plugins: [swMessagesPlugin()], + publicDir: false, + build: { + ...resolvedConfig.build, + lib: { + entry: swSrc, + formats: ['iife'], + name: 'sw_pleroma', + }, + emptyOutDir: false, + rolldownOptions: { + output: { + entryFileNames: swDest, + }, + }, + }, + configFile: false, + } + }, + generateBundle: { + order: 'post', + sequential: true, + async handler(_, bundle) { + const assets = Object.keys(bundle) + .filter((name) => !/\.map$/.test(name)) + .map((name) => '/' + name) + config.plugins.push({ + name: 'build-sw-env-plugin', + resolveId(id) { + if (id === swEnvName) { + return swEnvNameResolved + } + return null + }, + load(id) { + if (id === swEnvNameResolved) { + return getProdSwEnv({ assets }) + } + return null + }, + }) + }, + }, + closeBundle: { + order: 'post', + sequential: true, + async handler() { + console.info('Building service worker for production') + await build(config) + }, + }, + } +} + +const swMessagesName = 'virtual:pleroma-fe/service_worker_messages' +const swMessagesNameResolved = '\0' + swMessagesName + +export const swMessagesPlugin = () => { + return { + name: 'sw-messages-plugin', + resolveId(id) { + if (id === swMessagesName) { + Object.values(i18nFiles).forEach((f) => { + this.addWatchFile(f) + }) + return swMessagesNameResolved + } else { + return null + } + }, + async load(id) { + if (id === swMessagesNameResolved) { + return await getSWMessagesAsText() } return null }, } } - -export const swMessagesPlugin = () => { - const swMessagesName = 'virtual:pleroma-fe/service_worker_messages' - const swMessagesNameResolved = '\0' + swMessagesName - - return { - name: 'sw-messages-plugin', - resolveId: { - filter: { id: exactRegex(swMessagesName) }, - handler() { - return swMessagesNameResolved - } - }, - load: { - filter: { id: exactRegex(swMessagesNameResolved) }, - async handler () { - return await getSWMessagesAsText() - } - }, - } -} diff --git a/vite.config.js b/vite.config.js index f79f2327a..49a014abb 100644 --- a/vite.config.js +++ b/vite.config.js @@ -11,12 +11,12 @@ import { configDefaults } from 'vitest/config' import { getCommitHash } from './build/commit_hash.js' import copyPlugin from './build/copy_plugin.js' import emojisPlugin from './build/emojis_plugin.js' +import mswPlugin from './build/msw_plugin.js' import { + buildSwPlugin, devSwPlugin, swMessagesPlugin, } from './build/sw_plugin.js' -import { VitePWA } from 'vite-plugin-pwa' - const localConfigPath = '/config/local.json' const normalizeTarget = (target) => { @@ -142,6 +142,9 @@ export default defineConfig(async ({ mode, command }) => { // outDir: 'custom-dir', // optional, defaults to Vite's build.outDir }, }), + devSwPlugin({ swSrc, swDest, transformSW, alias }), + buildSwPlugin({ swSrc, swDest }), + swMessagesPlugin(), emojisPlugin(), copyPlugin({ inUrl: '/static/ruffle', @@ -160,8 +163,7 @@ export default defineConfig(async ({ mode, command }) => { 'node_modules/.cache/stylelintcache', ), }), - swMessagesPlugin(), - devSwPlugin({ swSrc, swDest }), + ...(mode === 'test' ? [mswPlugin()] : []), ], optimizeDeps: { // For unknown reasons, during vitest, vite will re-optimize the following @@ -206,9 +208,9 @@ export default defineConfig(async ({ mode, command }) => { devtools: {}, // enable devtools mode input: { main: 'index.html', - sw: 'src/sw.js', }, output: { + inlineDynamicImports: false, entryFileNames(chunkInfo) { const id = chunkInfo.facadeModuleId if (id.endsWith(swSrc)) { From a31e6f660aa7ca04a132cc88eedbd7928dccf37b Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 2 Jun 2026 23:53:11 +0300 Subject: [PATCH 2/2] lightened up notification utils by remove store stuff from it --- build/sw_plugin.js | 10 ++-------- src/components/mobile_nav/mobile_nav.js | 1 + src/components/notifications/notifications.js | 1 + src/modules/notifications.js | 2 ++ src/services/notification_utils/notification_utils.js | 8 ++++---- src/sw.js | 5 +++-- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/build/sw_plugin.js b/build/sw_plugin.js index 39cc31405..ba92993a7 100644 --- a/build/sw_plugin.js +++ b/build/sw_plugin.js @@ -1,7 +1,6 @@ import { readFile } from 'node:fs/promises' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import * as esbuild from 'esbuild' import { build } from 'vite' import { @@ -23,10 +22,6 @@ const getProdSwEnv = ({ assets }) => export const devSwPlugin = ({ swSrc, swDest, transformSW, alias }) => { const swFullSrc = resolve(projectRoot, swSrc) - const esbuildAlias = {} - Object.entries(alias).forEach(([source, dest]) => { - esbuildAlias[source] = dest.startsWith('/') ? projectRoot + dest : dest - }) return { name: 'dev-sw-plugin', @@ -58,12 +53,11 @@ export const devSwPlugin = ({ swSrc, swDest, transformSW, alias }) => { */ async transform(code, id) { if (id === swFullSrc && transformSW) { - const res = await esbuild.build({ + const res = await build({ entryPoints: [swSrc], bundle: true, write: false, outfile: 'sw-pleroma.js', - alias: esbuildAlias, plugins: [ { name: 'vite-like-root-resolve', @@ -140,7 +134,7 @@ export const buildSwPlugin = ({ swSrc, swDest }) => { formats: ['iife'], name: 'sw_pleroma', }, - emptyOutDir: false, + emptyOutDir: true, rolldownOptions: { output: { entryFileNames: swDest, diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index d25430a65..8aafd2709 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -66,6 +66,7 @@ const MobileNav = { countExtraNotifications( this.$store, useMergedConfigStore().mergedConfig, + useAnnouncementsStore().unreadAnnouncementCount, ) ) }, diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 96263d93b..9c50e645a 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -115,6 +115,7 @@ const Notifications = { return countExtraNotifications( this.$store, useMergedConfigStore().mergedConfig, + useAnnouncementsStore().unreadAnnouncementCount, ) }, unseenCountTitle() { diff --git a/src/modules/notifications.js b/src/modules/notifications.js index bf1e4b9c0..d501b39db 100644 --- a/src/modules/notifications.js +++ b/src/modules/notifications.js @@ -9,6 +9,7 @@ import { maybeShowNotification, } from '../services/notification_utils/notification_utils.js' +import { useI18nStore } from 'src/stores/i18n.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useReportsStore } from 'src/stores/reports.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' @@ -123,6 +124,7 @@ export const notifications = { useMergedConfigStore().mergedConfig.notificationVisibility, Object.values(useSyncConfigStore().prefsStorage.simple.muteFilters), notification, + useI18nStore().i18n, ) } else if (notification.seen) { state.idStore[notification.id].seen = true diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index 921600094..1149d7b9b 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -2,7 +2,6 @@ import { showDesktopNotification } from '../desktop_notification_utils/desktop_n import { muteFilterHits } from '../status_parser/status_parser.js' import { useAnnouncementsStore } from 'src/stores/announcements.js' -import { useI18nStore } from 'src/stores/i18n.js' import FaviconService from 'src/services/favicon_service/favicon_service.js' @@ -76,6 +75,7 @@ export const maybeShowNotification = ( notificationVisibility, muteFilters, notification, + i18n, ) => { const rootState = store.rootState || store.state @@ -89,7 +89,7 @@ export const maybeShowNotification = ( const notificationObject = prepareNotificationObject( notification, - useI18nStore().i18n, + i18n, ) showDesktopNotification(rootState, notificationObject) } @@ -193,7 +193,7 @@ export const prepareNotificationObject = (notification, i18n) => { return notifObj } -export const countExtraNotifications = (store, mergedConfig) => { +export const countExtraNotifications = (store, mergedConfig, unreadAnnouncementCount) => { const rootGetters = store.rootGetters || store.getters if (!mergedConfig.showExtraNotifications) { @@ -205,7 +205,7 @@ export const countExtraNotifications = (store, mergedConfig) => { ? rootGetters.unreadChatCount : 0, mergedConfig.showAnnouncementsInExtraNotifications - ? useAnnouncementsStore().unreadAnnouncementCount + ? unreadAnnouncementCount : 0, mergedConfig.showFollowRequestsInExtraNotifications ? rootGetters.followRequestCount diff --git a/src/sw.js b/src/sw.js index f1c1b75d2..cd0d6bb38 100644 --- a/src/sw.js +++ b/src/sw.js @@ -1,8 +1,9 @@ /* eslint-env serviceworker */ +import 'virtual:pleroma-fe/service_worker_env' + import { createI18n } from 'vue-i18n' -import { storage } from 'src/lib/storage.js' import { INSTANCE_DEFAULT_CONFIG } from 'src/modules/default_config_state.js' import { parseNotification } from 'src/services/entity_normalizer/entity_normalizer.service.js' import { prepareNotificationObject } from 'src/services/notification_utils/notification_utils.js' @@ -33,7 +34,7 @@ function getWindowClients() { } const setSettings = async () => { - const piniaState = await storage.getItem('pinia-local-sync_config') + const piniaState = {} const locale = piniaState.prefsStorage.simple.interfaceLanguage || 'en' i18n.locale = locale const notificationsNativeArray = Object.entries(