@docusaurus/plugin-pwa 2.0.0-beta.20 → 2.0.0-beta.21
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/lib/index.js +7 -8
- package/lib/options.js +6 -6
- package/lib/registerSw.d.ts +18 -0
- package/lib/registerSw.js +172 -249
- package/lib/renderReloadPopup.d.ts +8 -0
- package/lib/renderReloadPopup.js +8 -13
- package/lib/sw.d.ts +7 -0
- package/lib/sw.js +84 -120
- package/lib/theme/PwaReloadPopup/index.js +33 -17
- package/package.json +19 -16
- package/src/index.ts +10 -14
- package/src/options.ts +7 -6
- package/src/plugin-pwa.d.ts +0 -10
- package/src/{registerSw.js → registerSw.ts} +21 -11
- package/src/{renderReloadPopup.js → renderReloadPopup.tsx} +5 -4
- package/src/{sw.js → sw.ts} +31 -41
- package/src/theme/PwaReloadPopup/index.tsx +0 -1
package/lib/sw.js
CHANGED
|
@@ -5,143 +5,107 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
/* eslint-disable no-restricted-globals */
|
|
8
|
-
|
|
9
|
-
import {PrecacheController} from 'workbox-precaching';
|
|
10
|
-
|
|
8
|
+
import { PrecacheController } from 'workbox-precaching';
|
|
11
9
|
function parseSwParams() {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
return params;
|
|
10
|
+
const params = JSON.parse(new URLSearchParams(self.location.search).get('params'));
|
|
11
|
+
if (params.debug) {
|
|
12
|
+
console.log('[Docusaurus-PWA][SW]: Service Worker params:', params);
|
|
13
|
+
}
|
|
14
|
+
return params;
|
|
19
15
|
}
|
|
20
|
-
|
|
21
16
|
// Doc advises against dynamic imports in SW
|
|
22
17
|
// https://developers.google.com/web/tools/workbox/guides/using-bundlers#code_splitting_and_dynamic_imports
|
|
23
18
|
// https://twitter.com/sebastienlorber/status/1280155204575518720
|
|
24
19
|
// but looks it's working fine as it's inlined by webpack, need to double check
|
|
25
20
|
async function runSWCustomCode(params) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
if (process.env.PWA_SW_CUSTOM) {
|
|
22
|
+
const customSW = await import(process.env.PWA_SW_CUSTOM);
|
|
23
|
+
if (typeof customSW.default === 'function') {
|
|
24
|
+
customSW.default(params);
|
|
25
|
+
}
|
|
26
|
+
else if (params.debug) {
|
|
27
|
+
console.warn('[Docusaurus-PWA][SW]: swCustom should have a default export function');
|
|
28
|
+
}
|
|
34
29
|
}
|
|
35
|
-
}
|
|
36
30
|
}
|
|
37
|
-
|
|
38
31
|
/**
|
|
39
32
|
* Gets different possible variations for a request URL. Similar to
|
|
40
33
|
* https://git.io/JvixK
|
|
41
|
-
*
|
|
42
|
-
* @param {string} url
|
|
43
34
|
*/
|
|
44
35
|
function getPossibleURLs(url) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// /blog/ => /blog/index.html
|
|
60
|
-
if (urlObject.pathname.endsWith('/')) {
|
|
61
|
-
possibleURLs.push(`${urlObject.href}index.html`);
|
|
62
|
-
} else {
|
|
63
|
-
// /blog => /blog/index.html
|
|
64
|
-
possibleURLs.push(`${urlObject.href}/index.html`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return possibleURLs;
|
|
36
|
+
const urlObject = new URL(url, self.location.href);
|
|
37
|
+
if (urlObject.origin !== self.location.origin) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
// Ignore search params and hash
|
|
41
|
+
urlObject.search = '';
|
|
42
|
+
urlObject.hash = '';
|
|
43
|
+
return [
|
|
44
|
+
// /blog.html
|
|
45
|
+
urlObject.href,
|
|
46
|
+
// /blog/ => /blog/index.html
|
|
47
|
+
// /blog => /blog/index.html
|
|
48
|
+
`${urlObject.href}${urlObject.pathname.endsWith('/') ? '' : '/'}index.html`,
|
|
49
|
+
];
|
|
68
50
|
}
|
|
69
|
-
|
|
70
51
|
(async () => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
if (params.offlineMode) {
|
|
81
|
-
controller.addToCacheList(precacheManifest);
|
|
82
|
-
if (params.debug) {
|
|
83
|
-
console.log('[Docusaurus-PWA][SW]: addToCacheList', {
|
|
84
|
-
precacheManifest,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
await runSWCustomCode(params);
|
|
90
|
-
|
|
91
|
-
self.addEventListener('install', (event) => {
|
|
92
|
-
if (params.debug) {
|
|
93
|
-
console.log('[Docusaurus-PWA][SW]: install event', {
|
|
94
|
-
event,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
event.waitUntil(controller.install(event));
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
self.addEventListener('activate', (event) => {
|
|
101
|
-
if (params.debug) {
|
|
102
|
-
console.log('[Docusaurus-PWA][SW]: activate event', {
|
|
103
|
-
event,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
event.waitUntil(controller.activate(event));
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
self.addEventListener('fetch', async (event) => {
|
|
52
|
+
const params = parseSwParams();
|
|
53
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
54
|
+
const precacheManifest = self.__WB_MANIFEST;
|
|
55
|
+
const controller = new PrecacheController({
|
|
56
|
+
// Safer to turn this true?
|
|
57
|
+
fallbackToNetwork: true,
|
|
58
|
+
});
|
|
110
59
|
if (params.offlineMode) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const possibleURL = possibleURLs[i];
|
|
115
|
-
const cacheKey = controller.getCacheKeyForURL(possibleURL);
|
|
116
|
-
if (cacheKey) {
|
|
117
|
-
const cachedResponse = caches.match(cacheKey);
|
|
118
|
-
if (params.debug) {
|
|
119
|
-
console.log('[Docusaurus-PWA][SW]: serving cached asset', {
|
|
120
|
-
requestURL,
|
|
121
|
-
possibleURL,
|
|
122
|
-
possibleURLs,
|
|
123
|
-
cacheKey,
|
|
124
|
-
cachedResponse,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
event.respondWith(cachedResponse);
|
|
128
|
-
break;
|
|
60
|
+
controller.addToCacheList(precacheManifest);
|
|
61
|
+
if (params.debug) {
|
|
62
|
+
console.log('[Docusaurus-PWA][SW]: addToCacheList', { precacheManifest });
|
|
129
63
|
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
self.addEventListener('message', async (event) => {
|
|
135
|
-
if (params.debug) {
|
|
136
|
-
console.log('[Docusaurus-PWA][SW]: message event', {
|
|
137
|
-
event,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const type = event.data?.type;
|
|
142
|
-
|
|
143
|
-
if (type === 'SKIP_WAITING') {
|
|
144
|
-
self.skipWaiting();
|
|
145
64
|
}
|
|
146
|
-
|
|
65
|
+
await runSWCustomCode(params);
|
|
66
|
+
self.addEventListener('install', (event) => {
|
|
67
|
+
if (params.debug) {
|
|
68
|
+
console.log('[Docusaurus-PWA][SW]: install event', { event });
|
|
69
|
+
}
|
|
70
|
+
event.waitUntil(controller.install(event));
|
|
71
|
+
});
|
|
72
|
+
self.addEventListener('activate', (event) => {
|
|
73
|
+
if (params.debug) {
|
|
74
|
+
console.log('[Docusaurus-PWA][SW]: activate event', { event });
|
|
75
|
+
}
|
|
76
|
+
event.waitUntil(controller.activate(event));
|
|
77
|
+
});
|
|
78
|
+
self.addEventListener('fetch', async (event) => {
|
|
79
|
+
if (params.offlineMode) {
|
|
80
|
+
const requestURL = event.request.url;
|
|
81
|
+
const possibleURLs = getPossibleURLs(requestURL);
|
|
82
|
+
for (const possibleURL of possibleURLs) {
|
|
83
|
+
const cacheKey = controller.getCacheKeyForURL(possibleURL);
|
|
84
|
+
if (cacheKey) {
|
|
85
|
+
const cachedResponse = caches.match(cacheKey);
|
|
86
|
+
if (params.debug) {
|
|
87
|
+
console.log('[Docusaurus-PWA][SW]: serving cached asset', {
|
|
88
|
+
requestURL,
|
|
89
|
+
possibleURL,
|
|
90
|
+
possibleURLs,
|
|
91
|
+
cacheKey,
|
|
92
|
+
cachedResponse,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
event.respondWith(cachedResponse);
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
self.addEventListener('message', async (event) => {
|
|
102
|
+
if (params.debug) {
|
|
103
|
+
console.log('[Docusaurus-PWA][SW]: message event', { event });
|
|
104
|
+
}
|
|
105
|
+
const type = event.data?.type;
|
|
106
|
+
if (type === 'SKIP_WAITING') {
|
|
107
|
+
// lib def bug, see https://github.com/microsoft/TypeScript/issues/14877
|
|
108
|
+
self.skipWaiting();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
147
111
|
})();
|
|
@@ -4,35 +4,51 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import React, {
|
|
7
|
+
import React, {useState} from 'react';
|
|
8
8
|
import clsx from 'clsx';
|
|
9
|
-
import Translate, {
|
|
9
|
+
import Translate, {translate} from '@docusaurus/Translate';
|
|
10
10
|
import styles from './styles.module.css';
|
|
11
|
-
export default function PwaReloadPopup({
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
export default function PwaReloadPopup({onReload}) {
|
|
12
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
13
|
+
return (
|
|
14
|
+
isVisible && (
|
|
15
|
+
<div className={clsx('alert', 'alert--secondary', styles.popup)}>
|
|
14
16
|
<p>
|
|
15
|
-
<Translate
|
|
17
|
+
<Translate
|
|
18
|
+
id="theme.PwaReloadPopup.info"
|
|
19
|
+
description="The text for PWA reload popup">
|
|
16
20
|
New version available
|
|
17
21
|
</Translate>
|
|
18
22
|
</p>
|
|
19
23
|
<div className={styles.buttonContainer}>
|
|
20
|
-
<button
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
<button
|
|
25
|
+
className="button button--link"
|
|
26
|
+
type="button"
|
|
27
|
+
onClick={() => {
|
|
28
|
+
setIsVisible(false);
|
|
29
|
+
onReload();
|
|
30
|
+
}}>
|
|
31
|
+
<Translate
|
|
32
|
+
id="theme.PwaReloadPopup.refreshButtonText"
|
|
33
|
+
description="The text for PWA reload button">
|
|
25
34
|
Refresh
|
|
26
35
|
</Translate>
|
|
27
36
|
</button>
|
|
28
37
|
|
|
29
|
-
<button
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
<button
|
|
39
|
+
aria-label={translate({
|
|
40
|
+
id: 'theme.PwaReloadPopup.closeButtonAriaLabel',
|
|
41
|
+
message: 'Close',
|
|
42
|
+
description:
|
|
43
|
+
'The ARIA label for close button of PWA reload popup',
|
|
44
|
+
})}
|
|
45
|
+
className="close"
|
|
46
|
+
type="button"
|
|
47
|
+
onClick={() => setIsVisible(false)}>
|
|
34
48
|
<span aria-hidden="true">×</span>
|
|
35
49
|
</button>
|
|
36
50
|
</div>
|
|
37
|
-
</div>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
);
|
|
38
54
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/plugin-pwa",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.21",
|
|
4
4
|
"description": "Docusaurus Plugin to add PWA support.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/plugin-pwa.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "
|
|
9
|
-
"
|
|
10
|
-
"build:
|
|
11
|
-
"
|
|
8
|
+
"build": "tsc --build && node ../../admin/scripts/copyUntypedFiles.js && prettier --config ../../.prettierrc --write \"lib/theme/**/*.js\"",
|
|
9
|
+
"watch": "run-p -c copy:watch build:watch",
|
|
10
|
+
"build:watch": "tsc --build --watch",
|
|
11
|
+
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
|
12
12
|
},
|
|
13
13
|
"publishConfig": {
|
|
14
14
|
"access": "public"
|
|
@@ -20,31 +20,34 @@
|
|
|
20
20
|
},
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@babel/core": "^7.
|
|
24
|
-
"@babel/preset-env": "^7.
|
|
25
|
-
"@docusaurus/core": "2.0.0-beta.
|
|
26
|
-
"@docusaurus/theme-common": "2.0.0-beta.
|
|
27
|
-
"@docusaurus/theme-translations": "2.0.0-beta.
|
|
28
|
-
"@docusaurus/utils": "2.0.0-beta.
|
|
29
|
-
"@docusaurus/utils-validation": "2.0.0-beta.
|
|
23
|
+
"@babel/core": "^7.18.2",
|
|
24
|
+
"@babel/preset-env": "^7.18.2",
|
|
25
|
+
"@docusaurus/core": "2.0.0-beta.21",
|
|
26
|
+
"@docusaurus/theme-common": "2.0.0-beta.21",
|
|
27
|
+
"@docusaurus/theme-translations": "2.0.0-beta.21",
|
|
28
|
+
"@docusaurus/utils": "2.0.0-beta.21",
|
|
29
|
+
"@docusaurus/utils-validation": "2.0.0-beta.21",
|
|
30
30
|
"babel-loader": "^8.2.5",
|
|
31
31
|
"clsx": "^1.1.1",
|
|
32
|
-
"core-js": "^3.22.
|
|
32
|
+
"core-js": "^3.22.7",
|
|
33
33
|
"terser-webpack-plugin": "^5.3.1",
|
|
34
34
|
"tslib": "^2.4.0",
|
|
35
|
-
"webpack": "^5.72.
|
|
35
|
+
"webpack": "^5.72.1",
|
|
36
36
|
"webpack-merge": "^5.8.0",
|
|
37
37
|
"workbox-build": "^6.5.3",
|
|
38
38
|
"workbox-precaching": "^6.5.3",
|
|
39
39
|
"workbox-window": "^6.5.3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@docusaurus/module-type-aliases": "2.0.0-beta.
|
|
42
|
+
"@docusaurus/module-type-aliases": "2.0.0-beta.21",
|
|
43
43
|
"fs-extra": "^10.1.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"react": "^16.8.4 || ^17.0.0",
|
|
47
47
|
"react-dom": "^16.8.4 || ^17.0.0"
|
|
48
48
|
},
|
|
49
|
-
"
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=16.14"
|
|
51
|
+
},
|
|
52
|
+
"gitHead": "69ac49fc6909517f13615ee40290c4bd00c39df4"
|
|
50
53
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,18 +5,16 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {HtmlTags, LoadContext, Plugin} from '@docusaurus/types';
|
|
9
|
-
import type {PluginOptions} from '@docusaurus/plugin-pwa';
|
|
10
|
-
import {normalizeUrl} from '@docusaurus/utils';
|
|
11
|
-
import {compile} from '@docusaurus/core/lib/webpack/utils';
|
|
12
|
-
import LogPlugin from '@docusaurus/core/lib/webpack/plugins/LogPlugin';
|
|
13
|
-
import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations';
|
|
14
|
-
|
|
15
8
|
import path from 'path';
|
|
16
9
|
import webpack, {type Configuration} from 'webpack';
|
|
17
10
|
import Terser from 'terser-webpack-plugin';
|
|
18
|
-
|
|
19
11
|
import {injectManifest} from 'workbox-build';
|
|
12
|
+
import {normalizeUrl} from '@docusaurus/utils';
|
|
13
|
+
import {compile} from '@docusaurus/core/lib/webpack/utils';
|
|
14
|
+
import LogPlugin from '@docusaurus/core/lib/webpack/plugins/LogPlugin';
|
|
15
|
+
import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations';
|
|
16
|
+
import type {HtmlTags, LoadContext, Plugin} from '@docusaurus/types';
|
|
17
|
+
import type {PluginOptions} from '@docusaurus/plugin-pwa';
|
|
20
18
|
|
|
21
19
|
const isProd = process.env.NODE_ENV === 'production';
|
|
22
20
|
|
|
@@ -54,7 +52,6 @@ export default function pluginPWA(
|
|
|
54
52
|
debug,
|
|
55
53
|
offlineModeActivationStrategies,
|
|
56
54
|
injectManifestConfig,
|
|
57
|
-
reloadPopup,
|
|
58
55
|
pwaHead,
|
|
59
56
|
swCustom,
|
|
60
57
|
swRegister,
|
|
@@ -91,12 +88,11 @@ export default function pluginPWA(
|
|
|
91
88
|
new webpack.EnvironmentPlugin({
|
|
92
89
|
PWA_DEBUG: debug,
|
|
93
90
|
PWA_SERVICE_WORKER_URL: path.posix.resolve(
|
|
94
|
-
`${config.output?.publicPath || '/'}`,
|
|
91
|
+
`${(config.output?.publicPath as string) || '/'}`,
|
|
95
92
|
'sw.js',
|
|
96
93
|
),
|
|
97
94
|
PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES:
|
|
98
95
|
offlineModeActivationStrategies,
|
|
99
|
-
PWA_RELOAD_POPUP: reloadPopup,
|
|
100
96
|
}),
|
|
101
97
|
],
|
|
102
98
|
};
|
|
@@ -104,7 +100,7 @@ export default function pluginPWA(
|
|
|
104
100
|
|
|
105
101
|
injectHtmlTags() {
|
|
106
102
|
const headTags: HtmlTags = [];
|
|
107
|
-
if (isProd
|
|
103
|
+
if (isProd) {
|
|
108
104
|
pwaHead.forEach(({tagName, ...attributes}) => {
|
|
109
105
|
(['href', 'content'] as const).forEach((attribute) => {
|
|
110
106
|
const attributeValue = attributes[attribute];
|
|
@@ -162,7 +158,7 @@ export default function pluginPWA(
|
|
|
162
158
|
plugins: [
|
|
163
159
|
new webpack.EnvironmentPlugin({
|
|
164
160
|
// Fallback value required with Webpack 5
|
|
165
|
-
PWA_SW_CUSTOM: swCustom
|
|
161
|
+
PWA_SW_CUSTOM: swCustom ?? '',
|
|
166
162
|
}),
|
|
167
163
|
new LogPlugin({
|
|
168
164
|
name: 'Service Worker',
|
|
@@ -191,7 +187,7 @@ export default function pluginPWA(
|
|
|
191
187
|
'**/*.{png,jpg,jpeg,gif,svg,ico}',
|
|
192
188
|
'**/*.{woff,woff2,eot,ttf,otf}',
|
|
193
189
|
// @ts-expect-error: internal API?
|
|
194
|
-
...(injectManifest.globPatterns ?? []),
|
|
190
|
+
...((injectManifest.globPatterns as string[] | undefined) ?? []),
|
|
195
191
|
],
|
|
196
192
|
// Those attributes are not overrideable
|
|
197
193
|
swDest,
|
package/src/options.ts
CHANGED
|
@@ -20,10 +20,9 @@ const DEFAULT_OPTIONS = {
|
|
|
20
20
|
pwaHead: [],
|
|
21
21
|
swCustom: undefined,
|
|
22
22
|
swRegister: './registerSw.js',
|
|
23
|
-
reloadPopup: '@theme/PwaReloadPopup',
|
|
24
23
|
};
|
|
25
24
|
|
|
26
|
-
const
|
|
25
|
+
const optionsSchema = Joi.object<PluginOptions>({
|
|
27
26
|
debug: Joi.bool().default(DEFAULT_OPTIONS.debug),
|
|
28
27
|
offlineModeActivationStrategies: Joi.array()
|
|
29
28
|
.items(
|
|
@@ -49,14 +48,16 @@ const Schema = Joi.object({
|
|
|
49
48
|
swRegister: Joi.alternatives()
|
|
50
49
|
.try(Joi.string(), Joi.bool().valid(false))
|
|
51
50
|
.default(DEFAULT_OPTIONS.swRegister),
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.
|
|
51
|
+
// @ts-expect-error: forbidden
|
|
52
|
+
reloadPopup: Joi.any().forbidden().messages({
|
|
53
|
+
'any.unknown':
|
|
54
|
+
'The reloadPopup option is removed in favor of swizzling. See https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-pwa#customizing-reload-popup for how to customize the reload popup using swizzling.',
|
|
55
|
+
}),
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
export function validateOptions({
|
|
58
59
|
validate,
|
|
59
60
|
options,
|
|
60
61
|
}: OptionValidationContext<PluginOptions, PluginOptions>): PluginOptions {
|
|
61
|
-
return validate(
|
|
62
|
+
return validate(optionsSchema, options);
|
|
62
63
|
}
|
package/src/plugin-pwa.d.ts
CHANGED
|
@@ -46,16 +46,6 @@ declare module '@docusaurus/plugin-pwa' {
|
|
|
46
46
|
* @see https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.injectManifest
|
|
47
47
|
*/
|
|
48
48
|
injectManifestConfig: InjectManifestOptions;
|
|
49
|
-
/**
|
|
50
|
-
* Module path to reload popup component. This popup is rendered when a new
|
|
51
|
-
* service worker is waiting to be installed, and we suggest a reload to
|
|
52
|
-
* the user.
|
|
53
|
-
*
|
|
54
|
-
* Passing `false` will disable the popup, but this is not recommended:
|
|
55
|
-
* users won't have a way to get up-to-date content.
|
|
56
|
-
* @see {@link @theme/PwaReloadPopup}
|
|
57
|
-
*/
|
|
58
|
-
reloadPopup: string | false;
|
|
59
49
|
/**
|
|
60
50
|
* Array of objects containing `tagName` and key-value pairs for attributes
|
|
61
51
|
* to inject into the `<head>` tag. Technically you can inject any head tag
|
|
@@ -9,10 +9,9 @@ import {createStorageSlot} from '@docusaurus/theme-common';
|
|
|
9
9
|
|
|
10
10
|
// First: read the env variables (provided by Webpack)
|
|
11
11
|
/* eslint-disable prefer-destructuring */
|
|
12
|
-
const PWA_SERVICE_WORKER_URL = process.env.PWA_SERVICE_WORKER_URL
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
process.env.PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES;
|
|
12
|
+
const PWA_SERVICE_WORKER_URL = process.env.PWA_SERVICE_WORKER_URL!;
|
|
13
|
+
const PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES = process.env
|
|
14
|
+
.PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES as unknown as (keyof typeof OfflineModeActivationStrategiesImplementations)[];
|
|
16
15
|
const PWA_DEBUG = process.env.PWA_DEBUG;
|
|
17
16
|
/* eslint-enable prefer-destructuring */
|
|
18
17
|
|
|
@@ -34,7 +33,7 @@ async function clearRegistrations() {
|
|
|
34
33
|
}
|
|
35
34
|
await Promise.all(
|
|
36
35
|
registrations.map(async (registration) => {
|
|
37
|
-
const result = await registration
|
|
36
|
+
const result = await registration.unregister();
|
|
38
37
|
if (debug) {
|
|
39
38
|
console.log(
|
|
40
39
|
`[Docusaurus-PWA][registerSw]: unregister() service worker registration`,
|
|
@@ -65,6 +64,14 @@ https://stackoverflow.com/questions/51735869/check-if-user-has-already-installed
|
|
|
65
64
|
async function isAppInstalledEventFired() {
|
|
66
65
|
return AppInstalledEventFiredStorage.get() === 'true';
|
|
67
66
|
}
|
|
67
|
+
|
|
68
|
+
declare global {
|
|
69
|
+
interface Navigator {
|
|
70
|
+
getInstalledRelatedApps: () => Promise<{platform: string}[]>;
|
|
71
|
+
connection?: {effectiveType: string; saveData: boolean};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
68
75
|
async function isAppInstalledRelatedApps() {
|
|
69
76
|
if ('getInstalledRelatedApps' in window.navigator) {
|
|
70
77
|
const relatedApps = await navigator.getInstalledRelatedApps();
|
|
@@ -90,7 +97,9 @@ const OfflineModeActivationStrategiesImplementations = {
|
|
|
90
97
|
new URLSearchParams(window.location.search).get('offlineMode') === 'true',
|
|
91
98
|
};
|
|
92
99
|
|
|
93
|
-
async function isStrategyActive(
|
|
100
|
+
async function isStrategyActive(
|
|
101
|
+
strategyName: keyof typeof OfflineModeActivationStrategiesImplementations,
|
|
102
|
+
) {
|
|
94
103
|
return OfflineModeActivationStrategiesImplementations[strategyName]();
|
|
95
104
|
}
|
|
96
105
|
|
|
@@ -127,7 +136,7 @@ async function isOfflineModeEnabled() {
|
|
|
127
136
|
return enabled;
|
|
128
137
|
}
|
|
129
138
|
|
|
130
|
-
function createServiceWorkerUrl(params) {
|
|
139
|
+
function createServiceWorkerUrl(params: object) {
|
|
131
140
|
const paramsQueryString = JSON.stringify(params);
|
|
132
141
|
const url = `${PWA_SERVICE_WORKER_URL}?params=${encodeURIComponent(
|
|
133
142
|
paramsQueryString,
|
|
@@ -160,7 +169,7 @@ async function registerSW() {
|
|
|
160
169
|
// Immediately load new service worker when files aren't cached
|
|
161
170
|
if (!offlineMode) {
|
|
162
171
|
sendSkipWaiting();
|
|
163
|
-
} else
|
|
172
|
+
} else {
|
|
164
173
|
const renderReloadPopup = (await import('./renderReloadPopup')).default;
|
|
165
174
|
await renderReloadPopup({
|
|
166
175
|
onReload() {
|
|
@@ -173,7 +182,7 @@ async function registerSW() {
|
|
|
173
182
|
}
|
|
174
183
|
};
|
|
175
184
|
|
|
176
|
-
if (debug) {
|
|
185
|
+
if (debug && registration) {
|
|
177
186
|
if (registration.active) {
|
|
178
187
|
console.log(
|
|
179
188
|
'[Docusaurus-PWA][registerSw]: registration.active',
|
|
@@ -205,6 +214,7 @@ async function registerSW() {
|
|
|
205
214
|
|
|
206
215
|
// Update current service worker if the next one finishes installing and
|
|
207
216
|
// moves to waiting state in another tab.
|
|
217
|
+
// @ts-expect-error: not present in the API typings anymore
|
|
208
218
|
wb.addEventListener('externalwaiting', (event) => {
|
|
209
219
|
if (debug) {
|
|
210
220
|
console.log('[Docusaurus-PWA][registerSw]: event externalwaiting', event);
|
|
@@ -214,7 +224,7 @@ async function registerSW() {
|
|
|
214
224
|
|
|
215
225
|
// Update service worker if the next one is already in the waiting state.
|
|
216
226
|
// This happens when the user doesn't click on `reload` in the popup.
|
|
217
|
-
if (registration
|
|
227
|
+
if (registration?.waiting) {
|
|
218
228
|
await handleServiceWorkerWaiting();
|
|
219
229
|
}
|
|
220
230
|
}
|
|
@@ -301,6 +311,6 @@ if (typeof window !== 'undefined') {
|
|
|
301
311
|
addLegacyAppInstalledEventsListeners();
|
|
302
312
|
|
|
303
313
|
// Then try to register the SW using lazy/dynamic imports
|
|
304
|
-
registerSW().catch((e) => console.error('registerSW failed', e));
|
|
314
|
+
registerSW().catch((e: unknown) => console.error('registerSW failed', e));
|
|
305
315
|
}
|
|
306
316
|
}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React from 'react';
|
|
9
|
-
import
|
|
9
|
+
import ReactDOM from 'react-dom';
|
|
10
|
+
import type {Props} from '@theme/PwaReloadPopup';
|
|
10
11
|
|
|
11
12
|
const POPUP_CONTAINER_ID = 'pwa-popup-container';
|
|
12
13
|
|
|
@@ -19,8 +20,8 @@ const createContainer = () => {
|
|
|
19
20
|
return container;
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
export default async function renderReloadPopup(props) {
|
|
23
|
+
export default async function renderReloadPopup(props: Props): Promise<void> {
|
|
23
24
|
const container = getContainer() || createContainer();
|
|
24
|
-
const
|
|
25
|
-
render(<ReloadPopup {...props} />, container);
|
|
25
|
+
const ReloadPopup = (await import('@theme/PwaReloadPopup')).default;
|
|
26
|
+
ReactDOM.render(<ReloadPopup {...props} />, container);
|
|
26
27
|
}
|