Skip to content
Snippets Groups Projects
sw_plugin.js 4.32 KiB
Newer Older
tusooa's avatar
tusooa committed
import { fileURLToPath } from 'node:url'
import { dirname, resolve } from 'node:path'
tusooa's avatar
tusooa committed
import { readFile } from 'node:fs/promises'
import { build } from 'vite'
import * as esbuild from 'esbuild'
import { generateServiceWorkerMessages, i18nFiles } from './service_worker_messages.js'
tusooa's avatar
tusooa committed

const getSWMessagesAsText = async () => {
  const messages = await generateServiceWorkerMessages()
  return `export default ${JSON.stringify(messages, undefined, 2)}`
}
tusooa's avatar
tusooa committed
const projectRoot = dirname(dirname(fileURLToPath(import.meta.url)))

export const devSwPlugin = ({
  swSrc,
  swDest,
  transformSW,
  alias
tusooa's avatar
tusooa committed
}) => {
  const swFullSrc = resolve(projectRoot, swSrc)
  const esbuildAlias = {}
  Object.entries(alias).forEach(([source, dest]) => {
    esbuildAlias[source] = dest.startsWith('/') ? projectRoot + dest : dest
  })

tusooa's avatar
tusooa committed
  return {
    name: 'dev-sw-plugin',
    apply: 'serve',
    configResolved (conf) {
    },
tusooa's avatar
tusooa committed
    resolveId (id) {
      const name = id.startsWith('/') ? id.slice(1) : id
      if (name === swDest) {
        return swFullSrc
tusooa's avatar
tusooa committed
      }
      return null
    },
    async load (id) {
      if (id === swFullSrc) {
        return readFile(swFullSrc, 'utf-8')
tusooa's avatar
tusooa committed
      }
      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()
                }))
            }
          }]
        })
        const text = res.outputFiles[0].text
        return text
      }
    }
tusooa's avatar
tusooa committed
  }
}

// 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,
          rollupOptions: {
            output: {
              entryFileNames: swDest
            }
          }
        },
        configFile: false
      }
    },
    closeBundle: {
      order: 'post',
      sequential: true,
      async handler () {
        console.log('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)
        })
tusooa's avatar
tusooa committed
        return swMessagesNameResolved
      } else {
        return null
      }
    },
    async load (id) {
      if (id === swMessagesNameResolved) {
        return await getSWMessagesAsText()
tusooa's avatar
tusooa committed
      }
      return null
    }
  }
}