Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
21 KB
Referenced Files
None
Subscribers
None
diff --git a/build/sw_plugin.js b/build/sw_plugin.js
index e008c60c90..ee952d2293 100644
--- a/build/sw_plugin.js
+++ b/build/sw_plugin.js
@@ -1,208 +1,66 @@
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 { exactRegex } from '@rolldown/pluginutils'
import {
generateServiceWorkerMessages,
i18nFiles,
} from './service_worker_messages.js'
const getSWMessagesAsText = async () => {
const messages = await generateServiceWorkerMessages()
return `export default ${JSON.stringify(messages, undefined, 2)}`
}
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, transformSW, alias }) => {
+export const devSwPlugin = ({ swSrc, swDest }) => {
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 */
},
resolveId(id) {
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)
- },
- },
}
}
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/src/services/sw/sw.js b/src/services/sw/sw.js
index 68922bafb3..797f4c4487 100644
--- a/src/services/sw/sw.js
+++ b/src/services/sw/sw.js
@@ -1,187 +1,188 @@
/* global process */
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
const rawData = window.atob(base64)
return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)))
}
export function isSWSupported() {
return 'serviceWorker' in navigator
}
function isPushSupported() {
return 'PushManager' in window
}
function getOrCreateServiceWorker() {
if (!isSWSupported()) return
const swType = process.env.HAS_MODULE_SERVICE_WORKER ? 'module' : 'classic'
- return navigator.serviceWorker
- .register(import.meta.env.MODE === 'production' ? '/sw-pleroma.js' : '/dev-sw.js?dev-sw', { type: swType })
+ return navigator
+ .serviceWorker
+ .register('/sw-pleroma.js', { type: swType })
.catch((err) =>
console.error('Unable to get or create a service worker.', err),
)
}
function subscribePush(registration, isEnabled, vapidPublicKey) {
if (!isEnabled)
return Promise.reject(new Error('Web Push is disabled in config'))
if (!vapidPublicKey)
return Promise.reject(new Error('VAPID public key is not found'))
const subscribeOptions = {
userVisibleOnly: false,
applicationServerKey: urlBase64ToUint8Array(vapidPublicKey),
}
return registration.pushManager.subscribe(subscribeOptions)
}
function unsubscribePush(registration) {
return registration.pushManager.getSubscription().then((subscription) => {
if (subscription === null) {
return Promise.resolve('No subscription')
}
return subscription.unsubscribe()
})
}
function deleteSubscriptionFromBackEnd(token) {
return fetch('/api/v1/push/subscription/', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
}).then((response) => {
if (!response.ok) throw new Error('Bad status code from server.')
return response
})
}
function sendSubscriptionToBackEnd(
subscription,
token,
notificationVisibility,
) {
return window
.fetch('/api/v1/push/subscription/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
subscription,
data: {
alerts: {
follow: notificationVisibility.follows,
favourite: notificationVisibility.likes,
mention: notificationVisibility.mentions,
reblog: notificationVisibility.repeats,
move: notificationVisibility.moves,
},
},
}),
})
.then((response) => {
if (!response.ok) throw new Error('Bad status code from server.')
return response.json()
})
.then((responseData) => {
if (!responseData.id) throw new Error('Bad response from server.')
return responseData
})
}
export async function initServiceWorker(store) {
if (!isSWSupported()) return
await getOrCreateServiceWorker()
navigator.serviceWorker.addEventListener('message', (event) => {
const { dispatch } = store
const { type, ...rest } = event.data
switch (type) {
case 'notificationClicked':
dispatch('notificationClicked', { id: rest.id })
}
})
}
export async function showDesktopNotification(content) {
if (!isSWSupported) return
const { active: sw } =
(await window.navigator.serviceWorker.getRegistration()) || {}
if (!sw) return console.error('No serviceworker found!')
sw.postMessage({ type: 'desktopNotification', content })
}
export async function closeDesktopNotification({ id }) {
if (!isSWSupported) return
const { active: sw } =
(await window.navigator.serviceWorker.getRegistration()) || {}
if (!sw) return console.error('No serviceworker found!')
if (id >= 0) {
sw.postMessage({ type: 'desktopNotificationClose', content: { id } })
} else {
sw.postMessage({ type: 'desktopNotificationClose', content: { all: true } })
}
}
export async function updateFocus() {
if (!isSWSupported) return
const { active: sw } =
(await window.navigator.serviceWorker.getRegistration()) || {}
if (!sw) return console.error('No serviceworker found!')
sw.postMessage({ type: 'updateFocus' })
}
export function registerPushNotifications(
isEnabled,
vapidPublicKey,
token,
notificationVisibility,
) {
if (isPushSupported()) {
getOrCreateServiceWorker()
.then((registration) =>
subscribePush(registration, isEnabled, vapidPublicKey),
)
.then((subscription) =>
sendSubscriptionToBackEnd(subscription, token, notificationVisibility),
)
.catch((e) =>
console.warn(`Failed to setup Web Push Notifications: ${e.message}`),
)
}
}
export function unregisterPushNotifications(token) {
if (isPushSupported()) {
getOrCreateServiceWorker()
.then((registration) => {
return unsubscribePush(registration).then((result) => [
registration,
result,
])
})
.then(([, unsubResult]) => {
if (unsubResult === 'No subscription') return
if (!unsubResult) {
console.warn("Push subscription cancellation wasn't successful")
}
return deleteSubscriptionFromBackEnd(token)
})
.catch((e) => {
console.warn(`Failed to disable Web Push Notifications: ${e.message}`)
})
}
}
export const shouldCache = process.env.NODE_ENV === 'production'
export const cacheKey = 'pleroma-fe'
export const emojiCacheKey = 'pleroma-fe-emoji'
export const clearCache = (key) => caches.delete(key)
export { getOrCreateServiceWorker }
diff --git a/src/sw-pleroma.js b/src/sw.js
similarity index 100%
rename from src/sw-pleroma.js
rename to src/sw.js
diff --git a/vite.config.js b/vite.config.js
index 9672472151..f79f2327ad 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,278 +1,264 @@
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { defineConfig } from 'vite'
import { DevTools } from '@vitejs/devtools'
import eslint from 'vite-plugin-eslint2'
import stylelint from 'vite-plugin-stylelint'
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 {
+ devSwPlugin,
swMessagesPlugin,
} from './build/sw_plugin.js'
import { VitePWA } from 'vite-plugin-pwa'
const localConfigPath = '<projectRoot>/config/local.json'
const normalizeTarget = (target) => {
if (!target || typeof target !== 'string') return target
return target.endsWith('/') ? target.replace(/\/$/, '') : target
}
const getLocalDevSettings = async () => {
const envTarget = normalizeTarget(process.env.VITE_PROXY_TARGET)
const envOrigin = normalizeTarget(process.env.VITE_PROXY_ORIGIN)
try {
const settings = (await import('./config/local.json')).default
settings.target = normalizeTarget(settings.target)
settings.origin = normalizeTarget(settings.origin)
if (envTarget) settings.target = envTarget
if (envOrigin) settings.origin = envOrigin
console.info(`Using local dev server settings (${localConfigPath}):`)
console.info(JSON.stringify(settings, null, 2))
return settings
} catch (e) {
if (!envTarget && !envOrigin) {
console.info(
`Local dev server settings not found (${localConfigPath}), using default`,
e,
)
return {}
}
const settings = { target: envTarget, origin: envOrigin }
console.info(
'Using dev server settings from VITE_PROXY_TARGET/VITE_PROXY_ORIGIN:',
)
console.info(JSON.stringify(settings, null, 2))
return settings
}
}
const projectRoot = dirname(fileURLToPath(import.meta.url))
const getTransformSWSettings = (settings) => {
if ('transformSW' in settings) {
return settings.transformSW
} else {
console.info(
'`transformSW` is not present in your local settings.\n' +
'This option controls whether the service worker should be bundled and transformed into iife (immediately-invoked function expression) during development.\n' +
'If set to false, the service worker will be served as-is, as an ES Module.\n' +
'Some browsers (e.g. Firefox) does not support ESM service workers.\n' +
'To avoid surprises, it is defaulted to true, but this can be slow.\n' +
'If you are using a browser that supports ESM service workers, you can set this option to false.\n' +
`No matter your choice, you can set the transformSW option in ${localConfigPath} in to disable this message.`,
)
return true
}
}
export default defineConfig(async ({ mode, command }) => {
const settings = await getLocalDevSettings()
const target = settings.target || 'http://localhost:4000/'
const origin = settings.origin || target
const transformSW = getTransformSWSettings(settings)
const proxy = {
'/api': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
ws: true,
},
'/nodeinfo': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
},
'/instance': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
},
'/socket': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
ws: true,
headers: {
Origin: origin,
},
},
'/oauth': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
},
}
- const swSrc = 'src/sw-pleroma.js'
+ const swSrc = 'src/sw.js'
const swDest = 'sw-pleroma.js'
const alias = {
src: '/src',
components: '/src/components',
...(mode === 'test' ? { vue: 'vue/dist/vue.esm-bundler.js' } : {}),
}
return {
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement(tag) {
if (tag === 'pinch-zoom') {
return true
}
if (tag.startsWith('cropper-')) {
return true
}
return false
},
},
},
}),
vueJsx(),
DevTools({
build: {
withApp: true, // generate DevTools output during `vite build`
// outDir: 'custom-dir', // optional, defaults to Vite's build.outDir
},
}),
emojisPlugin(),
copyPlugin({
inUrl: '/static/ruffle',
inFs: resolve(projectRoot, 'node_modules/@ruffle-rs/ruffle'),
}),
eslint({
lintInWorker: true,
lintOnStart: true,
cacheLocation: resolve(projectRoot, 'node_modules/.cache/eslintcache'),
}),
stylelint({
lintInWorker: true,
lintOnStart: true,
cacheLocation: resolve(
projectRoot,
'node_modules/.cache/stylelintcache',
),
}),
swMessagesPlugin(),
- VitePWA({
- strategies: 'injectManifest',
- srcDir: 'src',
- filename: 'sw-pleroma.js',
- manifest: false,
- injectRegister: null,
- devOptions: {
- enabled: true,
- type: 'classic',
- },
- injectManifest: {
- injectionPoint: undefined,
- buildPlugins: {
- vite: [swMessagesPlugin()],
- },
- }
- }),
+ devSwPlugin({ swSrc, swDest }),
],
optimizeDeps: {
// For unknown reasons, during vitest, vite will re-optimize the following
// deps, causing the test to reload, so add them here so that it will not
// reload during tests
include: [
'custom-event-polyfill',
'vue-i18n',
'@ungap/event-target',
'lodash.merge',
'body-scroll-lock',
'@kazvmoe-infra/pinch-zoom-element',
],
},
css: {
devSourcemap: true,
},
resolve: {
alias,
},
define: {
'process.env': JSON.stringify({
NODE_ENV:
mode === 'test'
? 'testing'
: command === 'serve'
? 'development'
: 'production',
HAS_MODULE_SERVICE_WORKER: command === 'serve' && !transformSW,
}),
COMMIT_HASH: JSON.stringify(
command === 'serve' ? 'DEV' : getCommitHash(),
),
DEV_OVERRIDES: JSON.stringify(command === 'serve' ? settings : undefined),
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
},
build: {
sourcemap: true,
rolldownOptions: {
devtools: {}, // enable devtools mode
input: {
main: 'index.html',
+ sw: 'src/sw.js',
},
output: {
entryFileNames(chunkInfo) {
const id = chunkInfo.facadeModuleId
if (id.endsWith(swSrc)) {
return swDest
} else {
return 'static/js/[name].[hash].js'
}
},
chunkFileNames(chunkInfo) {
if (chunkInfo.facadeModuleId) {
if (
chunkInfo.facadeModuleId.includes(
'node_modules/@kazvmoe-infra/unicode-emoji-json/annotations/',
)
) {
return 'static/js/emoji-annotations/[name].[hash].js'
} else if (chunkInfo.facadeModuleId.includes('src/i18n/')) {
return 'static/js/i18n/[name].[hash].js'
}
}
return 'static/js/[name].[hash].js'
},
assetFileNames(assetInfo) {
const name = assetInfo.names?.[0] || ''
if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(name)) {
return 'static/img/[name].[hash][extname]'
} else if (/\.css$/.test(name)) {
return 'static/css/[name].[hash][extname]'
} else {
return 'static/misc/[name].[hash][extname]'
}
},
},
},
},
server: {
...(mode === 'test' ? {} : { proxy }),
port: Number(process.env.PORT) || 8080,
},
preview: {
proxy,
},
test: {
globals: true,
exclude: [...configDefaults.exclude, 'test/e2e-playwright/**'],
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'firefox' }],
},
},
}
})

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 1, 2:28 AM (13 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1802243
Default Alt Text
(21 KB)

Event Timeline