Skip to content

Commit

Permalink
feat(plugin-pwa): migrate pwa plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
meteorlxy committed Dec 11, 2020
1 parent bd9309b commit aa54fd6
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 0 deletions.
45 changes: 45 additions & 0 deletions packages/@vuepress/plugin-pwa/package.json
@@ -0,0 +1,45 @@
{
"name": "@vuepress/plugin-pwa",
"version": "2.0.0-alpha.7",
"description": "VuePress plugin - progressive web application",
"keywords": [
"vuepress",
"plugin",
"pwa"
],
"homepage": "https://github.com/vuepress",
"bugs": {
"url": "https://github.com/vuepress/vuepress-next/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vuepress/vuepress-next.git"
},
"license": "MIT",
"author": "meteorlxy",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib",
"styles"
],
"scripts": {
"build": "tsc -b tsconfig.build.json",
"clean": "rimraf lib *.tsbuildinfo"
},
"dependencies": {
"@vuepress/client": "2.0.0-alpha.7",
"@vuepress/core": "2.0.0-alpha.7",
"@vuepress/utils": "2.0.0-alpha.7",
"mitt": "^2.1.0",
"register-service-worker": "^1.7.2",
"vue": "^3.0.4",
"workbox-build": "^6.0.2"
},
"devDependencies": {
"@types/workbox-build": "^5.0.0"
},
"publishConfig": {
"access": "public"
}
}
67 changes: 67 additions & 0 deletions packages/@vuepress/plugin-pwa/src/clientAppSetup.ts
@@ -0,0 +1,67 @@
import mitt from 'mitt'
import { onMounted, provide } from 'vue'
import { withBase } from '@vuepress/client'
import type { ClientAppSetup } from '@vuepress/client'
import { pwaEventSymbol } from './composables'
import type { PwaEvent } from './composables'

declare const __DEV__: boolean
declare const __SSR__: boolean
declare const __PWA_SW_FILENAME__: string

const swFilename = __PWA_SW_FILENAME__

const clientAppSetup: ClientAppSetup = () => {
if (__DEV__ || __SSR__ || !swFilename) return

const log = (...args: any[]) => console.log('[@vuepress/plugin-pwa]', ...args)

// create event emitter and provide it
const event: PwaEvent = mitt()
provide(pwaEventSymbol, event)

onMounted(async () => {
// lazy load register-service-worker
const { register } = await import('register-service-worker')

// Register service worker
register(withBase(swFilename), {
ready(registration) {
log('Service worker is active.')
event.emit('ready', registration)
},

registered(registration) {
log('Service worker has been registered.')
event.emit('registered', registration)
},

cached(registration) {
log('Content has been cached for offline use.')
event.emit('cached', registration)
},

updatefound(registration) {
log('New content is downloading.')
event.emit('updatefound', registration)
},

updated(registration) {
log('New content is available, please refresh.')
event.emit('updated', registration)
},

offline() {
log('No internet connection found. App is running in offline mode.')
event.emit('offline')
},

error(err) {
log('Error during service worker registration:', err)
event.emit('error', err)
},
})
})
}

export default clientAppSetup
2 changes: 2 additions & 0 deletions packages/@vuepress/plugin-pwa/src/composables/index.ts
@@ -0,0 +1,2 @@
export * from './usePwaEvent'
export * from './useSkipWaiting'
32 changes: 32 additions & 0 deletions packages/@vuepress/plugin-pwa/src/composables/usePwaEvent.ts
@@ -0,0 +1,32 @@
import type { Emitter, Handler, WildcardHandler } from 'mitt'
import { inject } from 'vue'
import type { InjectionKey } from 'vue'

export interface PwaEvent extends Emitter {
on(type: 'ready', handler: Handler<ServiceWorkerRegistration>): void
on(type: 'registered', handler: Handler<ServiceWorkerRegistration>): void
on(type: 'cached', handler: Handler<ServiceWorkerRegistration>): void
on(type: 'updatefound', handler: Handler<ServiceWorkerRegistration>): void
on(type: 'updated', handler: Handler<ServiceWorkerRegistration>): void
on(type: 'offline', handler: Handler<void>): void
on(type: 'error', handler: Handler<Error>): void
on(type: '*', handler: WildcardHandler): void
emit(type: 'ready', event: ServiceWorkerRegistration): void
emit(type: 'registered', event: ServiceWorkerRegistration): void
emit(type: 'cached', event: ServiceWorkerRegistration): void
emit(type: 'updatefound', event: ServiceWorkerRegistration): void
emit(type: 'updated', event: ServiceWorkerRegistration): void
emit(type: 'offline'): void
emit(type: 'error', event: Error): void
emit(type: '*', event?: any): void
}

export const pwaEventSymbol: InjectionKey<PwaEvent> = Symbol('pwaEvent')

export const usePwaEvent = (): PwaEvent => {
const pawEvent = inject(pwaEventSymbol)
if (!pawEvent) {
throw new Error('usePwaEvent() is called without provider.')
}
return pawEvent
}
17 changes: 17 additions & 0 deletions packages/@vuepress/plugin-pwa/src/composables/useSkipWaiting.ts
@@ -0,0 +1,17 @@
/**
* Call `skipWaiting()` inside current waiting worker
*/
export const useSkipWaiting = (
registration: ServiceWorkerRegistration
): void => {
// get the waiting worker
const worker = registration.waiting

// if there is no waiting worker, return directly
if (!worker) return

// post SKIP_WAITING message to the waiting worker
const channel = new MessageChannel()

worker.postMessage({ type: 'SKIP_WAITING' }, [channel.port2])
}
83 changes: 83 additions & 0 deletions packages/@vuepress/plugin-pwa/src/index.ts
@@ -0,0 +1,83 @@
import type { Plugin } from '@vuepress/core'
import { logger, path, withSpinner } from '@vuepress/utils'
import type {
generateSW as GenerateSWFunc,
GenerateSWConfig,
} from 'workbox-build'

/**
* Options for @vuepress/plugin-pwa
*/
export interface PwaPluginOptions
extends Omit<GenerateSWConfig, 'swDest' | 'globDirectory'> {
/**
* Filename of the generated service worker file
*
* If you put it into a sub directory, the `scope` of service worker
* might be affected
*
* @default 'service-worker.js'
*/
serviceWorkerFilename?: string
}

const assetsExtensions = [
// basic
'html',
'js',
'css',
// images
'png',
'jpg',
'jpeg',
'gif',
'svg',
// fonts
'woff',
'woff2',
'eot',
'tff',
'otf',
]

export const pwaPlugin: Plugin<PwaPluginOptions> = ({
serviceWorkerFilename = 'service-worker.js',
...generateSWConfig
}) => ({
name: '@vuepress/plugin-pwa',

clientAppSetupFiles: path.resolve(__dirname, './clientAppSetup.js'),

define: {
__PWA_SW_FILENAME__: serviceWorkerFilename,
},

async onGenerated(app) {
await withSpinner('Generating service worker')(async () => {
// lazy-load workbox-build
const generateSW: typeof GenerateSWFunc = require('workbox-build/build/generate-sw')

const globDirectory = app.dir.dest()
const swDest = app.dir.dest(serviceWorkerFilename)

const { warnings } = await generateSW({
dontCacheBustURLsMatching: new RegExp(
`\\.[0-9a-f]{8}\\.(${assetsExtensions.join('|')})$`
),
globPatterns: [`**/*.{${assetsExtensions.join(',')}}`],
mode: app.env.isDebug ? 'development' : 'production',
sourcemap: app.env.isDebug,
...generateSWConfig,
// should not be override by user config
globDirectory,
swDest,
})

warnings.forEach((warning) =>
logger.warn('[@vuepress/plugin-pwa]', warning)
)
})
},
})

export default pwaPlugin
11 changes: 11 additions & 0 deletions packages/@vuepress/plugin-pwa/tsconfig.build.json
@@ -0,0 +1,11 @@
{
"extends": "../../../tsconfig.base.json",
"references": [
{ "path": "../client/tsconfig.build.json" },
{ "path": "../core/tsconfig.build.json" },
{ "path": "../utils/tsconfig.build.json" },
{ "path": "./tsconfig.esm.json" },
{ "path": "./tsconfig.cjs.json" }
],
"files": []
}
9 changes: 9 additions & 0 deletions packages/@vuepress/plugin-pwa/tsconfig.cjs.json
@@ -0,0 +1,9 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"module": "CommonJS",
"rootDir": "./src",
"outDir": "./lib"
},
"include": ["./src/index.ts"]
}
10 changes: 10 additions & 0 deletions packages/@vuepress/plugin-pwa/tsconfig.esm.json
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"module": "ES2020",
"rootDir": "./src",
"outDir": "./lib"
},
"include": ["./src"],
"exclude": ["./src/index.ts"]
}
4 changes: 4 additions & 0 deletions packages/@vuepress/plugin-pwa/tsconfig.json
@@ -0,0 +1,4 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["./src", "./__tests__"]
}
1 change: 1 addition & 0 deletions tsconfig.json
Expand Up @@ -22,6 +22,7 @@
{
"path": "./packages/@vuepress/plugin-palette-stylus/tsconfig.build.json"
},
{ "path": "./packages/@vuepress/plugin-pwa/tsconfig.build.json" },
{ "path": "./packages/@vuepress/shared/tsconfig.build.json" },
{ "path": "./packages/@vuepress/theme-default/tsconfig.build.json" },
{ "path": "./packages/@vuepress/theme-vue/tsconfig.build.json" },
Expand Down

0 comments on commit aa54fd6

Please sign in to comment.