@vite-pwa/nuxt 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.ts +48 -0
- package/dist/module.json +5 -0
- package/dist/module.mjs +147 -0
- package/dist/runtime/VitePwaManifest.d.ts +2 -0
- package/dist/runtime/VitePwaManifest.mjs +28 -0
- package/dist/types.d.ts +6 -0
- package/package.json +73 -0
- package/templates/pwa.client.ts +130 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<p align='center'>
|
|
2
|
+
<img src='./hero.png' alt="@vite-pwa/nuxt - Zero-config PWA for Nuxt 3"><br>
|
|
3
|
+
Zero-config PWA Plugin for Nuxt 3
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<p align='center'>
|
|
7
|
+
<a href='https://www.npmjs.com/package/@vite-pwa/nuxt' target="__blank">
|
|
8
|
+
<img src='https://img.shields.io/npm/v/@vite-pwa/nuxt?color=33A6B8&label=' alt="NPM version">
|
|
9
|
+
</a>
|
|
10
|
+
<a href="https://www.npmjs.com/package/@vite-pwa/nuxt" target="__blank">
|
|
11
|
+
<img alt="NPM Downloads" src="https://img.shields.io/npm/dm/@vite-pwa/nuxt?color=476582&label=">
|
|
12
|
+
</a>
|
|
13
|
+
<a href="https://vite-pwa-org.netlify.app/frameworks/nuxt" target="__blank">
|
|
14
|
+
<img src="https://img.shields.io/static/v1?label=&message=docs%20%26%20guides&color=2e859c" alt="Docs & Guides">
|
|
15
|
+
</a>
|
|
16
|
+
<br>
|
|
17
|
+
<a href="https://github.com/vite-pwa/nuxt" target="__blank">
|
|
18
|
+
<img alt="GitHub stars" src="https://img.shields.io/github/stars/vite-pwa/nuxt?style=social">
|
|
19
|
+
</a>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<br>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<a href="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg">
|
|
26
|
+
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg'/>
|
|
27
|
+
</a>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## 🚀 Features
|
|
32
|
+
|
|
33
|
+
- 📖 [**Documentation & guides**](https://vite-pwa-org.netlify.app/)
|
|
34
|
+
- 👌 **Zero-Config**: sensible built-in default configs for common use cases
|
|
35
|
+
- 🔩 **Extensible**: expose the full ability to customize the behavior of the plugin
|
|
36
|
+
- 🦾 **Type Strong**: written in [TypeScript](https://www.typescriptlang.org/)
|
|
37
|
+
- 🔌 **Offline Support**: generate service worker with offline support (via Workbox)
|
|
38
|
+
- ⚡ **Fully tree shakable**: auto inject Web App Manifest
|
|
39
|
+
- 💬 **Prompt for new content**: built-in support for Vanilla JavaScript, Vue 3, React, Svelte, SolidJS and Preact
|
|
40
|
+
- ⚙️ **Stale-while-revalidate**: automatic reload when new content is available
|
|
41
|
+
- ✨ **Static assets handling**: configure static assets for offline support
|
|
42
|
+
- 🐞 **Development Support**: debug your custom service worker logic as you develop your application
|
|
43
|
+
|
|
44
|
+
## 📦 Install
|
|
45
|
+
|
|
46
|
+
> Requires Vite 3.2.0+ and Nuxt 3.0.0+
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm i @vite-pwa/nuxt -D
|
|
50
|
+
|
|
51
|
+
# yarn
|
|
52
|
+
yarn add @vite-pwa/nuxt -D
|
|
53
|
+
|
|
54
|
+
# pnpm
|
|
55
|
+
pnpm add @vite-pwa/nuxt -D
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 🦄 Usage
|
|
59
|
+
|
|
60
|
+
Add `@vite-pwa/nuxt` module to `nuxt.config.ts` and configure it:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// nuxt.config.ts
|
|
64
|
+
import { defineNuxtConfig } from 'nuxt/config'
|
|
65
|
+
|
|
66
|
+
export default defineNuxtConfig({
|
|
67
|
+
modules: [
|
|
68
|
+
'@vite-pwa/nuxt'
|
|
69
|
+
],
|
|
70
|
+
pwa: {
|
|
71
|
+
/* PWA options */
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Read the [📖 documentation](https://vite-pwa-org.netlify.app/frameworks/nuxt) for a complete guide on how to configure and use
|
|
77
|
+
this plugin.
|
|
78
|
+
|
|
79
|
+
## 👀 Full config
|
|
80
|
+
|
|
81
|
+
Check out the type declaration [src/types.ts](./src/types.ts) and the following links for more details.
|
|
82
|
+
|
|
83
|
+
- [Web app manifests](https://developer.mozilla.org/en-US/docs/Web/Manifest)
|
|
84
|
+
- [Workbox](https://developers.google.com/web/tools/workbox)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
## 📄 License
|
|
88
|
+
|
|
89
|
+
MIT License © 2023-PRESENT [Anthony Fu](https://github.com/antfu)
|
package/dist/module.cjs
ADDED
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { VitePWAOptions } from 'vite-plugin-pwa';
|
|
3
|
+
|
|
4
|
+
interface ClientOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Exposes the plugin: defaults to true.
|
|
7
|
+
*/
|
|
8
|
+
registerPlugin?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Registers a periodic sync for updates interval: value in seconds.
|
|
11
|
+
*/
|
|
12
|
+
periodicSyncForUpdates?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Will prevent showing native PWA install prompt: defaults to false.
|
|
15
|
+
*
|
|
16
|
+
* When set to true or no empty string, the native PWA install prompt will be prevented.
|
|
17
|
+
*
|
|
18
|
+
* When set to a string, it will be used as the key in `localStorage` to prevent show the PWA install prompt widget.
|
|
19
|
+
*
|
|
20
|
+
* When set to true, the key used will be `vite-pwa:hide-install`.
|
|
21
|
+
*/
|
|
22
|
+
installPrompt?: boolean | string;
|
|
23
|
+
}
|
|
24
|
+
interface VitePWANuxtOptions extends Partial<VitePWAOptions> {
|
|
25
|
+
registerWebManifestInRouteRules?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Writes the plugin to disk: defaults to false (debug).
|
|
28
|
+
*/
|
|
29
|
+
writePlugin?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Options for plugin.
|
|
32
|
+
*/
|
|
33
|
+
client?: ClientOptions;
|
|
34
|
+
}
|
|
35
|
+
declare module '@nuxt/schema' {
|
|
36
|
+
interface NuxtConfig {
|
|
37
|
+
pwa?: {
|
|
38
|
+
[K in keyof VitePWANuxtOptions]?: Partial<VitePWANuxtOptions[K]>;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
interface NuxtOptions {
|
|
42
|
+
pwa: VitePWANuxtOptions;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare const _default: _nuxt_schema.NuxtModule<VitePWANuxtOptions>;
|
|
47
|
+
|
|
48
|
+
export { ClientOptions, VitePWANuxtOptions, _default as default };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver, addPluginTemplate, addComponent } from '@nuxt/kit';
|
|
2
|
+
import { VitePWA } from 'vite-plugin-pwa';
|
|
3
|
+
import { resolve } from 'pathe';
|
|
4
|
+
|
|
5
|
+
function configurePWAOptions(options, nuxt) {
|
|
6
|
+
if (!options.outDir) {
|
|
7
|
+
const publicDir = nuxt.options.nitro?.output?.publicDir;
|
|
8
|
+
options.outDir = publicDir ? resolve(publicDir) : resolve(nuxt.options.buildDir, "../.output/public");
|
|
9
|
+
}
|
|
10
|
+
let config;
|
|
11
|
+
if (options.strategies === "injectManifest") {
|
|
12
|
+
options.injectManifest = options.injectManifest ?? {};
|
|
13
|
+
config = options.injectManifest;
|
|
14
|
+
} else {
|
|
15
|
+
options.workbox = options.workbox ?? {};
|
|
16
|
+
if (options.registerType === "autoUpdate" && (options.injectRegister === "script" || options.injectRegister === "inline")) {
|
|
17
|
+
options.workbox.clientsClaim = true;
|
|
18
|
+
options.workbox.skipWaiting = true;
|
|
19
|
+
}
|
|
20
|
+
if (nuxt.options.dev) {
|
|
21
|
+
options.workbox.navigateFallback = nuxt.options.app.baseURL ?? "/";
|
|
22
|
+
if (options.devOptions?.enabled && !options.devOptions.navigateFallbackAllowlist)
|
|
23
|
+
options.devOptions.navigateFallbackAllowlist = [new RegExp(nuxt.options.app.baseURL) ?? /\//];
|
|
24
|
+
}
|
|
25
|
+
config = options.workbox;
|
|
26
|
+
}
|
|
27
|
+
if (!nuxt.options.dev)
|
|
28
|
+
config.manifestTransforms = [createManifestTransform(nuxt.options.app.baseURL ?? "/")];
|
|
29
|
+
}
|
|
30
|
+
function createManifestTransform(base) {
|
|
31
|
+
return async (entries) => {
|
|
32
|
+
entries.filter((e) => e && e.url.endsWith(".html")).forEach((e) => {
|
|
33
|
+
const url = e.url.startsWith("/") ? e.url.slice(1) : e.url;
|
|
34
|
+
if (url === "index.html") {
|
|
35
|
+
e.url = base;
|
|
36
|
+
} else {
|
|
37
|
+
const parts = url.split("/");
|
|
38
|
+
parts[parts.length - 1] = parts[parts.length - 1].replace(/\.html$/, "");
|
|
39
|
+
e.url = parts.length > 1 ? parts.slice(0, parts.length - 1).join("/") : parts[0];
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return { manifest: entries, warnings: [] };
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const module = defineNuxtModule({
|
|
47
|
+
meta: {
|
|
48
|
+
name: "pwa",
|
|
49
|
+
configKey: "pwa"
|
|
50
|
+
},
|
|
51
|
+
defaults: (nuxt) => ({
|
|
52
|
+
base: nuxt.options.app.baseURL,
|
|
53
|
+
scope: nuxt.options.app.baseURL,
|
|
54
|
+
injectRegister: false,
|
|
55
|
+
includeManifestIcons: false,
|
|
56
|
+
registerPlugin: true,
|
|
57
|
+
client: {
|
|
58
|
+
registerPlugin: true,
|
|
59
|
+
installPrompt: false,
|
|
60
|
+
periodicSyncForUpdates: 0
|
|
61
|
+
}
|
|
62
|
+
}),
|
|
63
|
+
async setup(options, nuxt) {
|
|
64
|
+
const resolver = createResolver(import.meta.url);
|
|
65
|
+
let vitePwaClientPlugin;
|
|
66
|
+
const resolveVitePluginPWAAPI = () => {
|
|
67
|
+
return vitePwaClientPlugin?.api;
|
|
68
|
+
};
|
|
69
|
+
const client = options.client ?? { registerPlugin: true, installPrompt: false, periodicSyncForUpdates: 0 };
|
|
70
|
+
if (client.registerPlugin) {
|
|
71
|
+
addPluginTemplate({
|
|
72
|
+
src: resolver.resolve("../templates/pwa.client.ts"),
|
|
73
|
+
write: true,
|
|
74
|
+
options: {
|
|
75
|
+
periodicSyncForUpdates: typeof client.periodicSyncForUpdates === "number" ? client.periodicSyncForUpdates : 0,
|
|
76
|
+
installPrompt: typeof client.installPrompt === "undefined" || client.installPrompt === false ? void 0 : client.installPrompt === true || client.installPrompt.trim() === "" ? "vite-pwa:hide-install" : client.installPrompt.trim()
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
await addComponent({
|
|
81
|
+
name: "VitePwaManifest",
|
|
82
|
+
filePath: resolver.resolve("./runtime/VitePwaManifest")
|
|
83
|
+
});
|
|
84
|
+
nuxt.hook("nitro:init", (nitro) => {
|
|
85
|
+
options.outDir = nitro.options.output.publicDir;
|
|
86
|
+
options.injectManifest = options.injectManifest || {};
|
|
87
|
+
options.injectManifest.globDirectory = nitro.options.output.publicDir;
|
|
88
|
+
});
|
|
89
|
+
nuxt.hook("vite:extend", ({ config }) => {
|
|
90
|
+
const plugin = config.plugins?.find((p) => p && typeof p === "object" && "name" in p && p.name === "vite-plugin-pwa");
|
|
91
|
+
if (plugin)
|
|
92
|
+
throw new Error("Remove vite-plugin-pwa plugin from Vite Plugins entry in Nuxt config file!");
|
|
93
|
+
});
|
|
94
|
+
nuxt.hook("vite:extendConfig", async (viteInlineConfig, { isClient }) => {
|
|
95
|
+
viteInlineConfig.plugins = viteInlineConfig.plugins || [];
|
|
96
|
+
const plugin = viteInlineConfig.plugins.find((p) => p && typeof p === "object" && "name" in p && p.name === "vite-plugin-pwa");
|
|
97
|
+
if (plugin)
|
|
98
|
+
throw new Error("Remove vite-plugin-pwa plugin from Vite Plugins entry in Nuxt config file!");
|
|
99
|
+
configurePWAOptions(options, nuxt);
|
|
100
|
+
const plugins = VitePWA(options);
|
|
101
|
+
viteInlineConfig.plugins.push(plugins);
|
|
102
|
+
if (isClient)
|
|
103
|
+
vitePwaClientPlugin = plugins.find((p) => p.name === "vite-plugin-pwa");
|
|
104
|
+
});
|
|
105
|
+
if (nuxt.options.dev) {
|
|
106
|
+
const webManifest = `${nuxt.options.app.baseURL}${options.devOptions?.webManifestUrl ?? options.manifestFilename ?? "manifest.webmanifest"}`;
|
|
107
|
+
const devSw = `${nuxt.options.app.baseURL}dev-sw.js?dev-sw`;
|
|
108
|
+
const workbox = `${nuxt.options.app.baseURL}workbox-`;
|
|
109
|
+
const emptyHandle = (_req, _res, next) => {
|
|
110
|
+
next();
|
|
111
|
+
};
|
|
112
|
+
nuxt.hook("vite:serverCreated", (viteServer, { isServer }) => {
|
|
113
|
+
if (isServer)
|
|
114
|
+
return;
|
|
115
|
+
viteServer.middlewares.stack.push({ route: webManifest, handle: emptyHandle });
|
|
116
|
+
viteServer.middlewares.stack.push({ route: devSw, handle: emptyHandle });
|
|
117
|
+
});
|
|
118
|
+
if (!options.strategies || options.strategies === "generateSW") {
|
|
119
|
+
nuxt.hook("vite:serverCreated", (viteServer, { isServer }) => {
|
|
120
|
+
if (isServer)
|
|
121
|
+
return;
|
|
122
|
+
viteServer.middlewares.stack.push({ route: workbox, handle: emptyHandle });
|
|
123
|
+
});
|
|
124
|
+
nuxt.hook("close", async () => {
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
if (options.registerWebManifestInRouteRules) {
|
|
129
|
+
nuxt.hook("nitro:config", async (nitroConfig) => {
|
|
130
|
+
nitroConfig.routeRules = nitroConfig.routeRules || {};
|
|
131
|
+
nitroConfig.routeRules[`${nuxt.options.app.baseURL}${options.manifestFilename ?? "manifest.webmanifest"}`] = {
|
|
132
|
+
headers: {
|
|
133
|
+
"Content-Type": "application/manifest+json"
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
nuxt.hook("nitro:init", (nitro) => {
|
|
139
|
+
nitro.hooks.hook("rollup:before", async () => {
|
|
140
|
+
await resolveVitePluginPWAAPI()?.generateSW();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
export { module as default };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import("vue").DefineComponent<{}, () => null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineComponent, ref } from "vue";
|
|
2
|
+
import { pwaInfo } from "virtual:pwa-info";
|
|
3
|
+
import { useHead } from "#imports";
|
|
4
|
+
export default defineComponent({
|
|
5
|
+
async setup() {
|
|
6
|
+
if (pwaInfo) {
|
|
7
|
+
const meta = ref({ link: [] });
|
|
8
|
+
useHead(meta);
|
|
9
|
+
const { webManifest } = pwaInfo;
|
|
10
|
+
if (webManifest) {
|
|
11
|
+
const { href, useCredentials } = webManifest;
|
|
12
|
+
if (useCredentials) {
|
|
13
|
+
meta.value.link.push({
|
|
14
|
+
rel: "manifest",
|
|
15
|
+
href,
|
|
16
|
+
crossorigin: "use-credentials"
|
|
17
|
+
});
|
|
18
|
+
} else {
|
|
19
|
+
meta.value.link.push({
|
|
20
|
+
rel: "manifest",
|
|
21
|
+
href
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return () => null;
|
|
27
|
+
}
|
|
28
|
+
});
|
package/dist/types.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vite-pwa/nuxt",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"packageManager": "pnpm@7.26.1",
|
|
6
|
+
"description": "Zero-config PWA for Nuxt 3",
|
|
7
|
+
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"funding": "https://github.com/sponsors/antfu",
|
|
10
|
+
"homepage": "https://github.com/vite-pwa/nuxt#readme",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/vite-pwa/nuxt.git"
|
|
14
|
+
},
|
|
15
|
+
"bugs": "https://github.com/vite-pwa/nuxt/issues",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"nuxt",
|
|
18
|
+
"pwa",
|
|
19
|
+
"workbox",
|
|
20
|
+
"vite-plugin-pwa",
|
|
21
|
+
"nuxt-module"
|
|
22
|
+
],
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"require": "./dist/module.cjs",
|
|
26
|
+
"import": "./dist/module.mjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"main": "./dist/module.cjs",
|
|
30
|
+
"types": "./dist/types.d.ts",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"templates"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"prepack": "nuxt-module-build",
|
|
37
|
+
"dev": "nuxi dev playground",
|
|
38
|
+
"dev:build": "nuxi build playground",
|
|
39
|
+
"dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
|
|
40
|
+
"release": "npm run lint && npm run prepack && npx bumpp --push --tag --commit && npm publish --access=public && git push --follow-tags",
|
|
41
|
+
"lint": "eslint .",
|
|
42
|
+
"lint-fix": "nr lint --fix"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@nuxt/kit": "^3.0.0 || ^3.1.0",
|
|
46
|
+
"vite-plugin-pwa": "^0.14.0"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@nuxt/kit": "^3.1.1"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@antfu/eslint-config": "^0.34.1",
|
|
53
|
+
"@antfu/ni": "^0.19.0",
|
|
54
|
+
"@nuxt/module-builder": "^0.2.1",
|
|
55
|
+
"@nuxt/schema": "^3.1.1",
|
|
56
|
+
"@nuxt/test-utils": "^3.1.1",
|
|
57
|
+
"changelogen": "^0.4.1",
|
|
58
|
+
"eslint": "^8.32.0",
|
|
59
|
+
"nuxt": "^3.1.1",
|
|
60
|
+
"typescript": "^4.9.4",
|
|
61
|
+
"vite-plugin-pwa": "^0.14.1"
|
|
62
|
+
},
|
|
63
|
+
"build": {
|
|
64
|
+
"externals": [
|
|
65
|
+
"node:child_process",
|
|
66
|
+
"node:fs",
|
|
67
|
+
"consola",
|
|
68
|
+
"pathe",
|
|
69
|
+
"ufo",
|
|
70
|
+
"vite-plugin-pwa"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { ref, reactive, nextTick } from 'vue'
|
|
2
|
+
import { useRegisterSW } from 'virtual:pwa-register/vue'
|
|
3
|
+
import { defineNuxtPlugin } from '#app'
|
|
4
|
+
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
const options: { periodicSyncForUpdates: number; installPrompt?: string } = <%= JSON.stringify(options) %>
|
|
7
|
+
|
|
8
|
+
export default defineNuxtPlugin(() => {
|
|
9
|
+
const registrationError = ref(false)
|
|
10
|
+
const swActivated = ref(false)
|
|
11
|
+
const showInstallPrompt = ref(false)
|
|
12
|
+
const hideInstall = ref(!options.installPrompt ? true : localStorage.getItem(options.installPrompt) === 'true')
|
|
13
|
+
|
|
14
|
+
// https://thomashunter.name/posts/2021-12-11-detecting-if-pwa-twa-is-installed
|
|
15
|
+
const ua = navigator.userAgent
|
|
16
|
+
const ios = ua.match(/iPhone|iPad|iPod/)
|
|
17
|
+
const standalone = window.matchMedia('(display-mode: standalone)').matches
|
|
18
|
+
const isInstalled = !!(standalone || (ios && !ua.match(/Safari/)))
|
|
19
|
+
|
|
20
|
+
const registerPeriodicSync = (swUrl: string, r: ServiceWorkerRegistration, timeout: number) => {
|
|
21
|
+
setInterval(async () => {
|
|
22
|
+
if (('connection' in navigator) && !navigator.onLine)
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
const resp = await fetch(swUrl, {
|
|
26
|
+
cache: 'no-store',
|
|
27
|
+
headers: {
|
|
28
|
+
'cache': 'no-store',
|
|
29
|
+
'cache-control': 'no-cache',
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
if (resp?.status === 200)
|
|
34
|
+
await r.update()
|
|
35
|
+
}, timeout)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const {
|
|
39
|
+
offlineReady, needRefresh, updateServiceWorker,
|
|
40
|
+
} = useRegisterSW({
|
|
41
|
+
immediate: true,
|
|
42
|
+
onRegisterError() {
|
|
43
|
+
registrationError.value = true
|
|
44
|
+
},
|
|
45
|
+
onRegisteredSW(swUrl, r) {
|
|
46
|
+
const timeout = options.periodicSyncForUpdates
|
|
47
|
+
if (timeout > 0) {
|
|
48
|
+
// should add support in pwa plugin
|
|
49
|
+
if (r?.active?.state === 'activated') {
|
|
50
|
+
swActivated.value = true
|
|
51
|
+
registerPeriodicSync(swUrl, r, timeout * 1000)
|
|
52
|
+
}
|
|
53
|
+
else if (r?.installing) {
|
|
54
|
+
r.installing.addEventListener('statechange', (e) => {
|
|
55
|
+
const sw = e.target as ServiceWorker
|
|
56
|
+
swActivated.value = sw.state === 'activated'
|
|
57
|
+
if (swActivated.value)
|
|
58
|
+
registerPeriodicSync(swUrl, r, timeout * 1000)
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const cancelPrompt = async () => {
|
|
66
|
+
offlineReady.value = false
|
|
67
|
+
needRefresh.value = false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let install: () => Promise<void> = () => Promise.resolve()
|
|
71
|
+
let cancelInstall: () => void = () => {}
|
|
72
|
+
|
|
73
|
+
if (!hideInstall.value) {
|
|
74
|
+
type InstallPromptEvent = Event & {
|
|
75
|
+
prompt: () => void
|
|
76
|
+
userChoice: Promise<{ outcome: 'dismissed' | 'accepted' }>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let deferredPrompt: InstallPromptEvent | undefined
|
|
80
|
+
|
|
81
|
+
const beforeInstallPrompt = (e: Event) => {
|
|
82
|
+
e.preventDefault()
|
|
83
|
+
deferredPrompt = e as InstallPromptEvent
|
|
84
|
+
showInstallPrompt.value = true
|
|
85
|
+
}
|
|
86
|
+
window.addEventListener('beforeinstallprompt', beforeInstallPrompt)
|
|
87
|
+
window.addEventListener('appinstalled', () => {
|
|
88
|
+
deferredPrompt = undefined
|
|
89
|
+
showInstallPrompt.value = false
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
cancelInstall = () => {
|
|
93
|
+
deferredPrompt = undefined
|
|
94
|
+
showInstallPrompt.value = false
|
|
95
|
+
window.removeEventListener('beforeinstallprompt', beforeInstallPrompt)
|
|
96
|
+
hideInstall.value = true
|
|
97
|
+
localStorage.setItem(options.installPrompt!, 'true')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
install = async () => {
|
|
101
|
+
if (!showInstallPrompt.value || !deferredPrompt) {
|
|
102
|
+
showInstallPrompt.value = false
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
showInstallPrompt.value = false
|
|
107
|
+
await nextTick()
|
|
108
|
+
deferredPrompt.prompt()
|
|
109
|
+
await deferredPrompt.userChoice
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
provide: {
|
|
116
|
+
pwa: reactive({
|
|
117
|
+
isInstalled,
|
|
118
|
+
showInstallPrompt,
|
|
119
|
+
cancelInstall,
|
|
120
|
+
install,
|
|
121
|
+
swActivated,
|
|
122
|
+
registrationError,
|
|
123
|
+
offlineReady,
|
|
124
|
+
needRefresh,
|
|
125
|
+
updateServiceWorker,
|
|
126
|
+
cancelPrompt,
|
|
127
|
+
}),
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
})
|