Bundle service worker conditionally

This commit is contained in:
tusooa 2025-02-28 13:29:27 -05:00
commit eb6d9cdd4b
No known key found for this signature in database
GPG key ID: 42AEC43D48433C51
4 changed files with 108 additions and 44 deletions

View file

@ -3,14 +3,20 @@ import { readFile } from 'node:fs/promises'
import { dirname, resolve } from 'node:path' import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
export const generateServiceWorkerMessages = async () => { const i18nDir = resolve(
const i18nDir = resolve(
dirname(dirname(fileURLToPath(import.meta.url))), dirname(dirname(fileURLToPath(import.meta.url))),
'src/i18n' 'src/i18n'
) )
const msgArray = await Promise.all(languages.map(async lang => {
export const i18nFiles = languages.reduce((acc, lang) => {
const name = langCodeToJsonName(lang) const name = langCodeToJsonName(lang)
const file = resolve(i18nDir, name + '.json') const file = resolve(i18nDir, name + '.json')
acc[lang] = file
return acc
}, {})
export const generateServiceWorkerMessages = async () => {
const msgArray = await Promise.all(Object.entries(i18nFiles).map(async ([lang, file]) => {
const fileContent = await readFile(file, 'utf-8') const fileContent = await readFile(file, 'utf-8')
const msg = { const msg = {
notifications: JSON.parse(fileContent).notifications || {} notifications: JSON.parse(fileContent).notifications || {}

View file

@ -1,28 +1,43 @@
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import { dirname } from 'node:path' import { dirname, resolve } from 'node:path'
import { readFile } from 'node:fs/promises' import { readFile } from 'node:fs/promises'
import { build } from 'vite' import { build } from 'vite'
import { generateServiceWorkerMessages } from './service_worker_messages.js' import * as esbuild from 'esbuild'
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 projectRoot = dirname(dirname(fileURLToPath(import.meta.url)))
export const devSwPlugin = ({ export const devSwPlugin = ({
swSrc, swSrc,
swDest, swDest,
transformSW,
alias
}) => { }) => {
const swFullSrc = resolve(projectRoot, swSrc)
const esbuildAlias = {}
Object.entries(alias).forEach(([source, dest]) => {
esbuildAlias[source] = dest.startsWith('/') ? projectRoot + dest : dest
})
return { return {
name: 'dev-sw-plugin', name: 'dev-sw-plugin',
apply: 'serve', apply: 'serve',
configResolved (conf) {
},
resolveId (id) { resolveId (id) {
const name = id.startsWith('/') ? id.slice(1) : id const name = id.startsWith('/') ? id.slice(1) : id
if (name === swDest) { if (name === swDest) {
return swSrc return swFullSrc
} }
return null return null
}, },
async load (id) { async load (id) {
if (id === swSrc) { if (id === swFullSrc) {
return readFile(swSrc, 'utf-8') return readFile(swFullSrc, 'utf-8')
} }
return null return null
}, },
@ -31,27 +46,45 @@ export const devSwPlugin = ({
* during dev, and firefox does not support ESM as service worker * during dev, and firefox does not support ESM as service worker
* https://bugzilla.mozilla.org/show_bug.cgi?id=1360870 * https://bugzilla.mozilla.org/show_bug.cgi?id=1360870
*/ */
// async transform (code, id) { async transform (code, id) {
// if (id === swSrc) { if (id === swFullSrc && transformSW) {
// console.log('load virtual') const res = await esbuild.build({
// const res = await build({ entryPoints: [swSrc],
// entryPoints: [swSrc], bundle: true,
// bundle: true, write: false,
// write: false, outfile: 'sw-pleroma.js',
// outfile: 'sw-pleroma.js', alias: esbuildAlias,
// alias: { plugins: [{
// 'src': projectRoot + '/src', name: 'vite-like-root-resolve',
// }, setup (b) {
// define: { b.onResolve(
// 'import.meta.glob': 'require' { filter: new RegExp(/^\//) },
// } args => ({
// }) path: resolve(projectRoot, args.path.slice(1))
// console.log('res', res) })
// const text = res.outputFiles[0].text )
// console.log('text', text) }
// return text }, {
// } 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()
}))
}
}]
})
const text = res.outputFiles[0].text
return text
}
}
} }
} }
@ -112,6 +145,9 @@ export const swMessagesPlugin = () => {
name: 'sw-messages-plugin', name: 'sw-messages-plugin',
resolveId (id) { resolveId (id) {
if (id === swMessagesName) { if (id === swMessagesName) {
Object.values(i18nFiles).forEach(f => {
this.addWatchFile(f)
})
return swMessagesNameResolved return swMessagesNameResolved
} else { } else {
return null return null
@ -119,8 +155,7 @@ export const swMessagesPlugin = () => {
}, },
async load (id) { async load (id) {
if (id === swMessagesNameResolved) { if (id === swMessagesNameResolved) {
const messages = await generateServiceWorkerMessages() return await getSWMessagesAsText()
return `export default ${JSON.stringify(messages, undefined, 2)}`
} }
return null return null
} }

View file

@ -4,6 +4,9 @@ import { storage } from 'src/lib/storage.js'
import { parseNotification } from './services/entity_normalizer/entity_normalizer.service.js' import { parseNotification } from './services/entity_normalizer/entity_normalizer.service.js'
import { prepareNotificationObject } from './services/notification_utils/notification_utils.js' import { prepareNotificationObject } from './services/notification_utils/notification_utils.js'
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
// Collects all messages for service workers
// Needed because service workers cannot use dynamic imports
// See /build/sw_plugin.js for more information
import messages from 'virtual:pleroma-fe/service_worker_messages' import messages from 'virtual:pleroma-fe/service_worker_messages'
const i18n = createI18n({ const i18n = createI18n({

View file

@ -8,6 +8,7 @@ import { VitePWA } from 'vite-plugin-pwa'
import { devSwPlugin, buildSwPlugin, swMessagesPlugin } from './build/sw_plugin.js' import { devSwPlugin, buildSwPlugin, swMessagesPlugin } from './build/sw_plugin.js'
import copyPlugin from './build/copy_plugin.js' import copyPlugin from './build/copy_plugin.js'
const localConfigPath = '<projectRoot>/config/local.json'
const getLocalDevSettings = async () => { const getLocalDevSettings = async () => {
try { try {
const settings = (await import('./config/local.json')).default const settings = (await import('./config/local.json')).default
@ -16,20 +17,38 @@ const getLocalDevSettings = async () => {
// and that's how actual BE reports its url // and that's how actual BE reports its url
settings.target = settings.target.replace(/\/$/, '') settings.target = settings.target.replace(/\/$/, '')
} }
console.info('Using local dev server settings (/config/local.json):') console.info(`Using local dev server settings (${localConfigPath}):`)
console.info(JSON.stringify(settings, null, 2)) console.info(JSON.stringify(settings, null, 2))
return settings return settings
} catch (e) { } catch (e) {
console.info('Local dev server settings not found (/config/local.json)', e) console.info(`Local dev server settings not found (${localConfigPath}), using default`, e)
return {} return {}
} }
} }
const projectRoot = dirname(fileURLToPath(import.meta.url)) 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 }) => { export default defineConfig(async ({ mode, command }) => {
const settings = await getLocalDevSettings() const settings = await getLocalDevSettings()
const target = settings.target || 'http://localhost:4000/' const target = settings.target || 'http://localhost:4000/'
const transformSW = getTransformSWSettings(settings)
const proxy = { const proxy = {
'/api': { '/api': {
target, target,
@ -60,6 +79,11 @@ export default defineConfig(async ({ mode, command }) => {
const swSrc = 'src/sw.js' const swSrc = 'src/sw.js'
const swDest = 'sw-pleroma.js' const swDest = 'sw-pleroma.js'
const alias = {
src: '/src',
components: '/src/components',
...(mode === 'test' ? { vue: 'vue/dist/vue.esm-bundler.js' } : {})
}
return { return {
plugins: [ plugins: [
@ -76,7 +100,7 @@ export default defineConfig(async ({ mode, command }) => {
} }
}), }),
vueJsx(), vueJsx(),
devSwPlugin({ swSrc, swDest }), devSwPlugin({ swSrc, swDest, transformSW, alias }),
buildSwPlugin({ swSrc, swDest }), buildSwPlugin({ swSrc, swDest }),
swMessagesPlugin(), swMessagesPlugin(),
copyPlugin({ copyPlugin({
@ -85,16 +109,12 @@ export default defineConfig(async ({ mode, command }) => {
}) })
], ],
resolve: { resolve: {
alias: { alias
src: '/src',
components: '/src/components',
...(mode === 'test' ? { vue: 'vue/dist/vue.esm-bundler.js' } : {})
}
}, },
define: { define: {
'process.env': JSON.stringify({ 'process.env': JSON.stringify({
NODE_ENV: command === 'serve' ? 'development' : 'production', NODE_ENV: command === 'serve' ? 'development' : 'production',
HAS_MODULE_SERVICE_WORKER: command === 'serve' HAS_MODULE_SERVICE_WORKER: command === 'serve' && !transformSW
}), }),
'COMMIT_HASH': JSON.stringify('DEV'), 'COMMIT_HASH': JSON.stringify('DEV'),
'DEV_OVERRIDES': JSON.stringify({}) 'DEV_OVERRIDES': JSON.stringify({})