@swoff/cli 0.3.4 → 0.3.6
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 +327 -26
- package/dist/__tests__/assemble-sw.test.js +41 -12
- package/dist/__tests__/assemble-sw.test.js.map +1 -1
- package/dist/__tests__/cli-integration.test.js +16 -18
- package/dist/__tests__/cli-integration.test.js.map +1 -1
- package/dist/__tests__/config-types.test.js +17 -26
- package/dist/__tests__/config-types.test.js.map +1 -1
- package/dist/__tests__/file-generators.test.js +71 -150
- package/dist/__tests__/file-generators.test.js.map +1 -1
- package/dist/__tests__/loader.test.js +15 -16
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/validator.test.js +54 -65
- package/dist/__tests__/validator.test.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/cli/help.js +6 -6
- package/dist/lib/cli/help.js.map +1 -1
- package/dist/lib/commands/add.js +19 -6
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/clean.js +5 -0
- package/dist/lib/commands/clean.js.map +1 -1
- package/dist/lib/commands/generate-guide.js +125 -0
- package/dist/lib/commands/generate-guide.js.map +1 -0
- package/dist/lib/commands/generate.js +11 -10
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/info.js +135 -30
- package/dist/lib/commands/info.js.map +1 -1
- package/dist/lib/commands/init.js +6 -12
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/commands/validate.js +7 -3
- package/dist/lib/commands/validate.js.map +1 -1
- package/dist/lib/config/validator.js +55 -65
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-fetch.js +29 -34
- package/dist/lib/generators/file-generators/auth-fetch.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-state.js +8 -5
- package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-store.js +71 -40
- package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-user.js +27 -31
- package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +9 -5
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +7 -2
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector.js +113 -0
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -0
- package/dist/lib/generators/file-generators/context.js +7 -6
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js +170 -40
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-hooks.js +225 -0
- package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -0
- package/dist/lib/generators/file-generators/guide-generator.js +333 -0
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/invalidation-tags.js +12 -10
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +34 -84
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-reconcile.js +57 -0
- package/dist/lib/generators/file-generators/mutation-reconcile.js.map +1 -0
- package/dist/lib/generators/file-generators/pwa-install.js +41 -9
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/quick-readme.js +78 -0
- package/dist/lib/generators/file-generators/quick-readme.js.map +1 -0
- package/dist/lib/generators/file-generators/reconcile.js +7 -12
- package/dist/lib/generators/file-generators/reconcile.js.map +1 -1
- package/dist/lib/generators/file-generators/store.js +31 -21
- package/dist/lib/generators/file-generators/store.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-generator-build.js +38 -17
- package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +82 -111
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-template.js +1 -1
- package/dist/lib/generators/file-generators/type-definitions.js +33 -10
- package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
- package/dist/lib/generators/sw-generator.js +19 -10
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +34 -13
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/config-header.js +2 -2
- package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +25 -10
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/tag-management.js +25 -5
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +10 -7
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +37 -26
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/utils/build-script.js +35 -0
- package/dist/lib/utils/build-script.js.map +1 -0
- package/dist/lib/utils/detect-framework.js +33 -0
- package/dist/lib/utils/detect-framework.js.map +1 -0
- package/package.json +1 -1
|
@@ -6,21 +6,22 @@ export function generateSwGeneratorBuild(ctx) {
|
|
|
6
6
|
const code = `#!/usr/bin/env node
|
|
7
7
|
/**
|
|
8
8
|
* Swoff SW Generator Build Script
|
|
9
|
-
* Reads swoff/sw
|
|
9
|
+
* Reads swoff/sw/template.js and generates versioned SW output.
|
|
10
10
|
*
|
|
11
11
|
* Add to package.json:
|
|
12
|
-
* "build": "your-build && node swoff/sw
|
|
12
|
+
* "build": "your-build && node swoff/sw/generator.js"
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
|
|
16
16
|
import { join, dirname, relative } from 'path';
|
|
17
|
+
import { createHash } from 'crypto';
|
|
17
18
|
import { fileURLToPath } from 'url';
|
|
18
19
|
|
|
19
20
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
21
|
const projectRoot = join(__dirname, '..');
|
|
21
22
|
|
|
22
23
|
const pkgPath = join(projectRoot, 'package.json');
|
|
23
|
-
const templatePath = join(__dirname, '
|
|
24
|
+
const templatePath = join(__dirname, 'template.js');
|
|
24
25
|
const configPath = join(projectRoot, 'swoff.config.json');
|
|
25
26
|
|
|
26
27
|
if (!existsSync(configPath)) {
|
|
@@ -30,7 +31,7 @@ if (!existsSync(configPath)) {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
if (!existsSync(templatePath)) {
|
|
33
|
-
console.error('Error: swoff/sw
|
|
34
|
+
console.error('Error: swoff/sw/template.js not found');
|
|
34
35
|
process.exit(1);
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -38,7 +39,12 @@ const pkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, 'utf8')) : {
|
|
|
38
39
|
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
39
40
|
const template = readFileSync(templatePath, 'utf8');
|
|
40
41
|
|
|
41
|
-
const
|
|
42
|
+
const swConfig = config.features?.serviceWorker || {};
|
|
43
|
+
const versionConfig = swConfig.version || {};
|
|
44
|
+
const versionEnabled = versionConfig.enabled !== false;
|
|
45
|
+
const version = versionConfig.source === 'manual' && versionConfig.value
|
|
46
|
+
? versionConfig.value
|
|
47
|
+
: pkg.version || '1.0.0';
|
|
42
48
|
const outputDir = config.build?.outputDir || 'dist';
|
|
43
49
|
const swFilename = config.build?.swFilename || 'sw';
|
|
44
50
|
|
|
@@ -61,8 +67,12 @@ function collectAssets(dir) {
|
|
|
61
67
|
return assets;
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
function generateCacheNameHash(swContent) {
|
|
71
|
+
return 'sw-cache-' + createHash('sha256').update(swContent).digest('hex').slice(0, 12);
|
|
72
|
+
}
|
|
73
|
+
|
|
64
74
|
const allAssets = collectAssets(outDir);
|
|
65
|
-
const swFile = \`\${swFilename}-v\${version}.js\`;
|
|
75
|
+
const swFile = versionEnabled ? \`\${swFilename}-v\${version}.js\` : \`\${swFilename}.js\`;
|
|
66
76
|
const filtered = allAssets.filter(a => !a.endsWith(swFile) && a !== '/version.json');
|
|
67
77
|
const fallback = ['/index.html'];
|
|
68
78
|
if (config.features?.pwa?.enabled) fallback.push('/manifest.json');
|
|
@@ -70,19 +80,30 @@ const combined = [...new Set([...fallback, ...filtered])];
|
|
|
70
80
|
const assetsToCache = combined.map(url => ({ url, options: {} }));
|
|
71
81
|
|
|
72
82
|
let sw = template;
|
|
73
|
-
|
|
83
|
+
if (versionEnabled) {
|
|
84
|
+
sw = sw.replace('// [[CACHE_NAME]]', \`CACHE_NAME = 'sw-v\${version}'\`);
|
|
85
|
+
} else {
|
|
86
|
+
const sentinel = 'SW_CACHE_SENTINEL';
|
|
87
|
+
sw = sw.replace('// [[CACHE_NAME]]', \`CACHE_NAME = '\${sentinel}'\`);
|
|
88
|
+
}
|
|
74
89
|
sw = sw.replace('// [[ASSETS_LIST]]', \`ASSETS_TO_CACHE = \${JSON.stringify(assetsToCache, null, 2)}\`);
|
|
75
|
-
sw = sw.replace('// [[AUTO_SKIP_WAITING]]', \`const AUTO_SKIP_WAITING = \${config.serviceWorker?.autoActivate || false};\`);
|
|
90
|
+
sw = sw.replace('// [[AUTO_SKIP_WAITING]]', \`const AUTO_SKIP_WAITING = \${config.features?.serviceWorker?.autoActivate || false};\`);
|
|
76
91
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
92
|
+
if (!versionEnabled) {
|
|
93
|
+
const cacheName = generateCacheNameHash(sw);
|
|
94
|
+
sw = sw.replace(sentinel, cacheName);
|
|
95
|
+
writeFileSync(join(outDir, swFile), sw);
|
|
96
|
+
console.log(\`Service worker built: \${outputDir}/\${swFile}\`);
|
|
97
|
+
} else {
|
|
98
|
+
writeFileSync(join(outDir, swFile), sw);
|
|
99
|
+
writeFileSync(join(outDir, 'version.json'), JSON.stringify({
|
|
100
|
+
version,
|
|
101
|
+
minSupportedVersion: versionConfig.minSupportedVersion || '0.0.0',
|
|
102
|
+
generatedAt: new Date().toISOString(),
|
|
103
|
+
}, null, 2));
|
|
104
|
+
console.log(\`Service worker built: \${outputDir}/\${swFile}\`);
|
|
105
|
+
}
|
|
85
106
|
`;
|
|
86
|
-
writeFile(ctx, "sw
|
|
107
|
+
writeFile(ctx, "sw/generator.js", code);
|
|
87
108
|
}
|
|
88
109
|
//# sourceMappingURL=sw-generator-build.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sw-generator-build.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-generator-build.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,IAAI,GAAG
|
|
1
|
+
{"version":3,"file":"sw-generator-build.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-generator-build.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,GAAqB;IAC5D,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoGd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generates sw
|
|
2
|
+
* Generates sw/injector.{js|ts} - core SW registration logic only.
|
|
3
3
|
*/
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
5
|
export function generateSwInjector(ctx) {
|
|
6
|
-
const
|
|
7
|
-
const autoActivate = ctx.config.serviceWorker.autoActivate;
|
|
8
|
-
const
|
|
9
|
-
const preventDefaultInstall = ctx.config.features.pwa.preventDefaultInstall;
|
|
10
|
-
const crossTabSync = ctx.config.features.crossTabSync;
|
|
6
|
+
const autoUpdate = ctx.config.features.serviceWorker.autoUpdate;
|
|
7
|
+
const autoActivate = ctx.config.features.serviceWorker.autoActivate;
|
|
8
|
+
const versionEnabled = ctx.config.features.serviceWorker.version.enabled;
|
|
11
9
|
const ext = ctx.ext;
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
10
|
+
const ts = ext === "ts";
|
|
11
|
+
const T = (type) => (ts ? `: ${type}` : "");
|
|
12
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
13
|
+
const versionedCode = `/**
|
|
15
14
|
* Swoff SW Injector
|
|
16
|
-
* Framework-agnostic SW registration
|
|
15
|
+
* Framework-agnostic SW registration with versioned URLs and update flow.
|
|
17
16
|
*
|
|
18
17
|
* Usage:
|
|
19
|
-
* import { initServiceWorker } from './swoff/sw
|
|
20
|
-
*
|
|
21
|
-
* // Call in your app entry point (e.g., main.tsx, app.js):
|
|
18
|
+
* import { initServiceWorker } from './swoff/sw/injector.${ext}';
|
|
22
19
|
* initServiceWorker();
|
|
23
20
|
*
|
|
24
21
|
* Window events:
|
|
@@ -27,32 +24,28 @@ export function generateSwInjector(ctx) {
|
|
|
27
24
|
* sw-progress - Download progress (detail: { percent, downloaded, total })
|
|
28
25
|
* sw-ready - SW active and controlling page
|
|
29
26
|
* sw-error - SW registration failed
|
|
30
|
-
* pwa-installable - PWA can be installed (detail: { isInstallable: true })
|
|
31
|
-
* pwa-installed - User accepted install (detail: { outcome: 'accepted' })
|
|
32
|
-
* cache-invalidated - Cache entries with given tags cleared (detail: { tags })
|
|
33
|
-
* mutation-sync-complete - Queued mutations synced (detail: { succeeded, failed })
|
|
34
|
-
* mutation-queue-changed - Queue modified
|
|
35
|
-
* mutation-rollback - Mutation exhausted retries (detail: { method, url, tempId, previousData })
|
|
36
|
-
* sw-auth-unauthorized - 401 response received (token expired or invalid)${authEnabled ? `
|
|
37
|
-
* sw-auth-state-change - Login or logout (detail: { authenticated: boolean })` : ""}
|
|
38
27
|
*
|
|
39
28
|
* Window properties:
|
|
40
29
|
* window.latestSWVersion - Latest version from version.json
|
|
41
30
|
* window.currentSWVersion - Active SW version
|
|
42
31
|
* window.swAvailableVersion - Pending update version
|
|
43
32
|
* window.swUpdateRequired - Forced update needed (version < minSupportedVersion)
|
|
44
|
-
* window.swMinSupportedVersion - Minimum supported version
|
|
33
|
+
* window.swMinSupportedVersion - Minimum supported version
|
|
45
34
|
* window.swReady - SW is active
|
|
46
35
|
* window.swError - Registration failed
|
|
47
|
-
* window.deferredInstallPrompt - Captured BeforeInstallPromptEvent
|
|
48
|
-
* window.pwaInstallable - Whether PWA can be installed
|
|
49
36
|
*/
|
|
50
|
-
|
|
51
|
-
` : ""}
|
|
52
|
-
const AUTO_REGISTER = ${autoRegister};
|
|
37
|
+
const AUTO_UPDATE = ${autoUpdate};
|
|
53
38
|
const AUTO_ACTIVATE = ${autoActivate};
|
|
54
39
|
|
|
55
|
-
|
|
40
|
+
function semverCompare(a${T("string")}, b${T("string")})${R("number")}{
|
|
41
|
+
const pa = a.split('.').map(Number);
|
|
42
|
+
const pb = b.split('.').map(Number);
|
|
43
|
+
for (let i = 0; i < 3; i++) {
|
|
44
|
+
if (pa[i] > pb[i]) return 1;
|
|
45
|
+
if (pa[i] < pb[i]) return -1;
|
|
46
|
+
}
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
56
49
|
|
|
57
50
|
async function checkForUpdate() {
|
|
58
51
|
const response = await fetch("/version.json");
|
|
@@ -62,17 +55,17 @@ async function checkForUpdate() {
|
|
|
62
55
|
return response.json();
|
|
63
56
|
}
|
|
64
57
|
|
|
65
|
-
async function waitForController()
|
|
66
|
-
return new Promise((resolve) => {
|
|
58
|
+
async function waitForController()${R("Promise<void>")}{
|
|
59
|
+
return new Promise<void>((resolve) => {
|
|
67
60
|
if (navigator.serviceWorker.controller) {
|
|
68
61
|
resolve();
|
|
69
62
|
} else {
|
|
70
|
-
navigator.serviceWorker.addEventListener("controllerchange", resolve, { once: true });
|
|
63
|
+
navigator.serviceWorker.addEventListener("controllerchange", () => resolve(), { once: true });
|
|
71
64
|
}
|
|
72
65
|
});
|
|
73
66
|
}
|
|
74
67
|
|
|
75
|
-
async function doRegisterServiceWorker(version)
|
|
68
|
+
async function doRegisterServiceWorker(version${T("string")})${R("Promise<ServiceWorkerRegistration>")}{
|
|
76
69
|
const swUrl = \`/sw-v\${version}.js\`;
|
|
77
70
|
const registration = await navigator.serviceWorker.register(swUrl);
|
|
78
71
|
localStorage.setItem("swRegisteredVersion", version);
|
|
@@ -84,18 +77,13 @@ async function doRegisterServiceWorker(version) {
|
|
|
84
77
|
return registration;
|
|
85
78
|
}
|
|
86
79
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export async function initServiceWorker() {
|
|
80
|
+
/** Register the SW with version checking and update flow. Checks version.json, handles updates, and dispatches sw-ready/sw-error events. */
|
|
81
|
+
export async function initServiceWorker()${R("Promise<void>")}{
|
|
92
82
|
if (!("serviceWorker" in navigator)) {
|
|
93
83
|
console.warn("Service Workers not supported");
|
|
94
84
|
return;
|
|
95
85
|
}
|
|
96
86
|
|
|
97
|
-
if (!shouldRegister()) return;
|
|
98
|
-
|
|
99
87
|
try {
|
|
100
88
|
const manifest = await checkForUpdate();
|
|
101
89
|
const currentVersion = localStorage.getItem("swRegisteredVersion");
|
|
@@ -105,7 +93,7 @@ export async function initServiceWorker() {
|
|
|
105
93
|
if (currentVersion === manifest.version) {
|
|
106
94
|
const registration = await navigator.serviceWorker.getRegistration();
|
|
107
95
|
if (registration && registration.active) {
|
|
108
|
-
window.currentSWVersion = currentVersion;
|
|
96
|
+
window.currentSWVersion = currentVersion ?? undefined;
|
|
109
97
|
window.dispatchEvent(new CustomEvent("sw-version-detected"));
|
|
110
98
|
await waitForController();
|
|
111
99
|
if (!window.swReady) {
|
|
@@ -115,9 +103,9 @@ export async function initServiceWorker() {
|
|
|
115
103
|
} else if (currentVersion && currentVersion !== manifest.version) {
|
|
116
104
|
window.swAvailableVersion = manifest.version;
|
|
117
105
|
window.swUpdateRequired =
|
|
118
|
-
currentVersion
|
|
106
|
+
semverCompare(currentVersion, manifest.minSupportedVersion || "0.0.0") < 0;
|
|
119
107
|
|
|
120
|
-
if (
|
|
108
|
+
if (AUTO_UPDATE) {
|
|
121
109
|
const registration = await navigator.serviceWorker.getRegistration();
|
|
122
110
|
if (registration && registration.waiting) {
|
|
123
111
|
if (AUTO_ACTIVATE) {
|
|
@@ -163,7 +151,9 @@ export async function initServiceWorker() {
|
|
|
163
151
|
}
|
|
164
152
|
return;
|
|
165
153
|
}
|
|
166
|
-
} catch {
|
|
154
|
+
} catch {
|
|
155
|
+
// Handle the case where no existing active registration is found
|
|
156
|
+
}
|
|
167
157
|
|
|
168
158
|
console.error("Service Worker initialization failed:", error);
|
|
169
159
|
window.swError = true;
|
|
@@ -171,7 +161,8 @@ export async function initServiceWorker() {
|
|
|
171
161
|
}
|
|
172
162
|
}
|
|
173
163
|
|
|
174
|
-
|
|
164
|
+
/** Accept a pending SW update. Reloads the page once the new SW takes control. */
|
|
165
|
+
export async function handleUpdateApproved(newVersion${T("string")})${R("Promise<void>")}{
|
|
175
166
|
const registration = await navigator.serviceWorker.getRegistration();
|
|
176
167
|
if (registration && registration.waiting) {
|
|
177
168
|
registration.waiting.postMessage({ type: "SKIP_WAITING" });
|
|
@@ -186,81 +177,61 @@ export async function handleUpdateApproved(newVersion) {
|
|
|
186
177
|
}
|
|
187
178
|
}
|
|
188
179
|
|
|
189
|
-
|
|
180
|
+
/** Activate a waiting SW without reloading. Useful when you handle the transition yourself. */
|
|
181
|
+
export async function skipWaiting()${R("Promise<void>")}{
|
|
190
182
|
const registration = await navigator.serviceWorker.ready;
|
|
191
183
|
if (registration.waiting) {
|
|
192
184
|
registration.waiting.postMessage({ type: "SKIP_WAITING" });
|
|
193
185
|
}
|
|
194
186
|
}
|
|
187
|
+
`;
|
|
188
|
+
const simpleCode = `/**
|
|
189
|
+
* Swoff SW Injector (Simple Mode)
|
|
190
|
+
* Fixed SW URL with hash-based cache name — no versioned URLs or update events.
|
|
191
|
+
*
|
|
192
|
+
* Usage:
|
|
193
|
+
* import { initServiceWorker } from './swoff/sw/injector.${ext}';
|
|
194
|
+
* initServiceWorker();
|
|
195
|
+
*
|
|
196
|
+
* Window events:
|
|
197
|
+
* sw-ready - SW active and controlling page
|
|
198
|
+
* sw-error - SW registration failed
|
|
199
|
+
*
|
|
200
|
+
* Window properties:
|
|
201
|
+
* window.currentSWVersion - "simple"
|
|
202
|
+
* window.swReady - SW is active
|
|
203
|
+
* window.swError - Registration failed
|
|
204
|
+
*/
|
|
205
|
+
async function waitForController()${R("Promise<void>")}{
|
|
206
|
+
return new Promise<void>((resolve) => {
|
|
207
|
+
if (navigator.serviceWorker.controller) {
|
|
208
|
+
resolve();
|
|
209
|
+
} else {
|
|
210
|
+
navigator.serviceWorker.addEventListener("controllerchange", () => resolve(), { once: true });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
195
214
|
|
|
196
|
-
|
|
197
|
-
${
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
215
|
+
/** Register the SW with a fixed URL (non-versioned mode). Dispatches sw-ready/sw-error. */
|
|
216
|
+
export async function initServiceWorker()${R("Promise<void>")}{
|
|
217
|
+
if (!("serviceWorker" in navigator)) {
|
|
218
|
+
console.warn("Service Workers not supported");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const registration = await navigator.serviceWorker.register("/sw.js");
|
|
223
|
+
window.currentSWVersion = "simple";
|
|
224
|
+
window.dispatchEvent(new CustomEvent("sw-version-detected"));
|
|
225
|
+
await waitForController();
|
|
226
|
+
window.dispatchEvent(new CustomEvent("sw-ready"));
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error("Service Worker registration failed:", error);
|
|
229
|
+
window.swError = true;
|
|
230
|
+
window.dispatchEvent(new CustomEvent("sw-error"));
|
|
204
231
|
}
|
|
205
|
-
|
|
206
|
-
window.dispatchEvent(
|
|
207
|
-
new CustomEvent("pwa-installable", {
|
|
208
|
-
detail: { isInstallable: true },
|
|
209
|
-
})
|
|
210
|
-
);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
window.addEventListener("appinstalled", () => {
|
|
214
|
-
window.deferredInstallPrompt = null;
|
|
215
|
-
window.pwaInstallable = false;
|
|
216
|
-
|
|
217
|
-
window.dispatchEvent(
|
|
218
|
-
new CustomEvent("pwa-installed", {
|
|
219
|
-
detail: { outcome: "accepted" },
|
|
220
|
-
})
|
|
221
|
-
);
|
|
222
|
-
});
|
|
223
|
-
` : ""}
|
|
224
|
-
${mutationQueueEnabled ? `
|
|
225
|
-
// --- Mutation Queue Listener ---
|
|
226
|
-
window.addEventListener("online", processMutationQueue);
|
|
227
|
-
` : ""}
|
|
228
|
-
// --- SW Message Listener ---
|
|
229
|
-
if (typeof window !== "undefined" && "serviceWorker" in navigator) {
|
|
230
|
-
navigator.serviceWorker.addEventListener("message", (event) => {
|
|
231
|
-
if (event.data.type === "SW_PROGRESS") {
|
|
232
|
-
const { percent, downloaded, total } = event.data;
|
|
233
|
-
window.dispatchEvent(
|
|
234
|
-
new CustomEvent("sw-progress", {
|
|
235
|
-
detail: { percent, downloaded, total },
|
|
236
|
-
})
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
if (event.data.type === "BACKGROUND_SYNC_COMPLETE") {
|
|
240
|
-
const { succeeded, failed, tags } = event.data.detail;
|
|
241
|
-
window.dispatchEvent(
|
|
242
|
-
new CustomEvent("mutation-sync-complete", {
|
|
243
|
-
detail: { succeeded, failed },
|
|
244
|
-
})
|
|
245
|
-
);
|
|
246
|
-
if (tags && tags.length > 0) {
|
|
247
|
-
window.dispatchEvent(
|
|
248
|
-
new CustomEvent("cache-invalidated", { detail: { tags } })
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
|
|
252
|
-
}
|
|
253
|
-
${crossTabSync ? `
|
|
254
|
-
if (event.data.type === "TAG_INVALIDATED" && event.data.tag) {
|
|
255
|
-
window.dispatchEvent(
|
|
256
|
-
new CustomEvent("cache-invalidated", {
|
|
257
|
-
detail: { tags: [event.data.tag] },
|
|
258
|
-
})
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
` : ""} });
|
|
262
232
|
}
|
|
263
233
|
`;
|
|
264
|
-
|
|
234
|
+
const code = versionEnabled ? versionedCode : simpleCode;
|
|
235
|
+
writeFile(ctx, `sw/injector.${ext}`, code);
|
|
265
236
|
}
|
|
266
237
|
//# sourceMappingURL=sw-injector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sw-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,
|
|
1
|
+
{"version":3,"file":"sw-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/sw-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC;IAChE,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC;IACpE,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC;IACzE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IAExB,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEtD,MAAM,aAAa,GAAG;;;;;8DAKsC,GAAG;;;;;;;;;;;;;;;;;;;sBAmB3C,UAAU;wBACR,YAAY;;0BAEV,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;oCAkBjC,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;gDAUN,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oCAAoC,CAAC;;;;;;;;;;;;;2CAa3D,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uDAoFN,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;qCAgBnD,CAAC,CAAC,eAAe,CAAC;;;;;;CAMtD,CAAC;IAEA,MAAM,UAAU,GAAG;;;;;8DAKyC,GAAG;;;;;;;;;;;;oCAY7B,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;2CAWX,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;CAiB5D,CAAC;IAEA,MAAM,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC;IAEzD,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -11,6 +11,6 @@ export function generateSwTemplate(ctx) {
|
|
|
11
11
|
code = code.replace(`CACHE_NAME = 'sw-v${placeholderVersion}'`, "// [[CACHE_NAME]]");
|
|
12
12
|
code = code.replace(/^ASSETS_TO_CACHE = \[[\s\S]*?\]$/m, "// [[ASSETS_LIST]]");
|
|
13
13
|
code = code.replace(/^const AUTO_SKIP_WAITING = (true|false);?$/m, "// [[AUTO_SKIP_WAITING]]");
|
|
14
|
-
writeFile(ctx, "sw
|
|
14
|
+
writeFile(ctx, "sw/template.js", code);
|
|
15
15
|
}
|
|
16
16
|
//# sourceMappingURL=sw-template.js.map
|
|
@@ -5,17 +5,35 @@ import { writeFile } from "./context.js";
|
|
|
5
5
|
export function generateTypeDefinitions(ctx) {
|
|
6
6
|
if (ctx.ext !== "ts")
|
|
7
7
|
return;
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const code = `
|
|
9
|
+
// Background Sync API types
|
|
10
|
+
interface SyncManager {
|
|
11
|
+
register(tag: string): Promise<void>;
|
|
12
|
+
getTags(): Promise<string[]>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface SyncEvent extends Event {
|
|
16
|
+
readonly tag: string;
|
|
17
|
+
readonly lastChance: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ServiceWorkerGlobalScope {
|
|
21
|
+
onsync: ((this: ServiceWorkerGlobalScope, ev: SyncEvent) => unknown) | null;
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
declare global {
|
|
25
|
+
interface BeforeInstallPromptEvent extends Event {
|
|
26
|
+
prompt(): Promise<void>;
|
|
27
|
+
userChoice: Promise<{ outcome: "accepted" | "dismissed" }>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface WindowEventMap {
|
|
31
|
+
beforeinstallprompt: BeforeInstallPromptEvent;
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
interface Window {
|
|
18
35
|
deferredInstallPrompt: BeforeInstallPromptEvent | null;
|
|
36
|
+
pwaInstallable?: boolean;
|
|
19
37
|
latestSWVersion?: string;
|
|
20
38
|
currentSWVersion?: string;
|
|
21
39
|
swRegisteredVersion?: string;
|
|
@@ -23,7 +41,15 @@ declare global {
|
|
|
23
41
|
swUpdateRequired?: boolean;
|
|
24
42
|
swMinSupportedVersion?: string;
|
|
25
43
|
swReady?: boolean;
|
|
26
|
-
swError?: boolean
|
|
44
|
+
swError?: boolean;
|
|
45
|
+
swAuthState?: "authenticated" | "unauthenticated" | "loading";
|
|
46
|
+
swCurrentUser?: Record<string, unknown> | null;
|
|
47
|
+
SyncManager?: { new(): SyncManager };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Extend ServiceWorkerRegistration in the global scope for TS
|
|
51
|
+
interface ServiceWorkerRegistration {
|
|
52
|
+
readonly sync: SyncManager;
|
|
27
53
|
}
|
|
28
54
|
}
|
|
29
55
|
|
|
@@ -52,12 +78,9 @@ export interface MutationQueueItem {
|
|
|
52
78
|
url: string;
|
|
53
79
|
body: unknown;
|
|
54
80
|
headers: Record<string, string>;
|
|
55
|
-
previousData: unknown | null;
|
|
56
81
|
timestamp: number;
|
|
57
82
|
retryCount: number;
|
|
58
83
|
tags: string[];
|
|
59
|
-
storeName: string | null;
|
|
60
|
-
tempId: string | null;
|
|
61
84
|
}
|
|
62
85
|
|
|
63
86
|
export interface MutationQueueResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-definitions.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/type-definitions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,uBAAuB,CAAC,GAAqB;IAC3D,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI;QAAE,OAAO;IAE7B,MAAM,
|
|
1
|
+
{"version":3,"file":"type-definitions.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/type-definitions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,uBAAuB,CAAC,GAAqB;IAC3D,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI;QAAE,OAAO;IAE7B,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoFd,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -22,6 +22,12 @@ const configPathArg = args.findIndex((arg) => arg === "--config-path");
|
|
|
22
22
|
const passedProjectRoot = projectRootArg !== -1 ? args[projectRootArg + 1] : null;
|
|
23
23
|
const passedConfigPath = configPathArg !== -1 ? args[configPathArg + 1] : null;
|
|
24
24
|
const projectRoot = passedProjectRoot || process.cwd();
|
|
25
|
+
function resolveVersion(config, pkgVersion) {
|
|
26
|
+
const v = config.features.serviceWorker.version;
|
|
27
|
+
if (v.source === "manual" && v.value)
|
|
28
|
+
return v.value;
|
|
29
|
+
return pkgVersion || "1.0.0";
|
|
30
|
+
}
|
|
25
31
|
export async function generateSW(options = {}) {
|
|
26
32
|
const optProjectRoot = options.projectRoot || projectRoot;
|
|
27
33
|
const optConfigPath = options.configPath || passedConfigPath || undefined;
|
|
@@ -41,23 +47,26 @@ export async function generateSW(options = {}) {
|
|
|
41
47
|
status("Swoff config generation disabled. Using custom code mode.");
|
|
42
48
|
return { version: "", outputFile: "" };
|
|
43
49
|
}
|
|
44
|
-
const
|
|
50
|
+
const versionEnabled = config.features.serviceWorker.version.enabled;
|
|
51
|
+
const version = resolveVersion(config, pkg.version || "1.0.0");
|
|
45
52
|
const sw = assembleSW(config, version, optProjectRoot);
|
|
46
53
|
const outputDir = join(optProjectRoot, config.build.outputDir);
|
|
47
|
-
const swFilename = config.build.swFilename;
|
|
48
54
|
if (!existsSync(outputDir)) {
|
|
49
55
|
mkdirSync(outputDir, { recursive: true });
|
|
50
56
|
}
|
|
51
|
-
const
|
|
57
|
+
const swFilename = config.build.swFilename;
|
|
58
|
+
const outputFile = versionEnabled ? `${swFilename}-v${version}.js` : `${swFilename}.js`;
|
|
52
59
|
try {
|
|
53
60
|
writeFileSync(join(outputDir, outputFile), sw);
|
|
54
|
-
|
|
55
|
-
version,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
if (versionEnabled) {
|
|
62
|
+
writeFileSync(join(outputDir, "version.json"), JSON.stringify({
|
|
63
|
+
version,
|
|
64
|
+
minSupportedVersion: config.features.serviceWorker.version.minSupportedVersion,
|
|
65
|
+
generatedAt: new Date().toISOString(),
|
|
66
|
+
configEnabled: config.enabled,
|
|
67
|
+
configSource,
|
|
68
|
+
}, null, 2));
|
|
69
|
+
}
|
|
61
70
|
status(`✓ ${outputDir}/${outputFile}`);
|
|
62
71
|
}
|
|
63
72
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sw-generator.js","sourceRoot":"","sources":["../../../src/lib/generators/sw-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAW,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAQ1D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,gBAAgB,CAAC,CAAC;AACzE,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,eAAe,CAAC,CAAC;AAEvE,MAAM,iBAAiB,GAAG,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAClF,MAAM,gBAAgB,GAAG,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAE/E,MAAM,WAAW,GAAG,iBAAiB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;AAEvD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAA4B,EAAE;IAC7D,MAAM,cAAc,GAAG,OAAO,CAAC,WAAW,IAAI,WAAW,CAAC;IAC1D,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,IAAI,gBAAgB,IAAI,SAAS,CAAC;IAC1E,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC;IAE/C,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;IACrD,IAAI,GAAG,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAE/B,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,oDAAoD,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,UAAU,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IAE3E,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,CAAC,2DAA2D,CAAC,CAAC;QACpE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IACzC,CAAC;IAED,MAAM,
|
|
1
|
+
{"version":3,"file":"sw-generator.js","sourceRoot":"","sources":["../../../src/lib/generators/sw-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { readdirSync, existsSync } from "fs";
|
|
6
6
|
import { join, relative } from "path";
|
|
7
|
+
import { createHash } from "crypto";
|
|
7
8
|
import { getDefaultTemplate } from "./default-template.js";
|
|
8
9
|
import { generateConfigHeader } from "./config-header.js";
|
|
9
10
|
import { generateInstallHandler } from "./install-handler.js";
|
|
@@ -28,10 +29,30 @@ function collectAssets(dir, baseDir) {
|
|
|
28
29
|
}
|
|
29
30
|
return assets;
|
|
30
31
|
}
|
|
32
|
+
function generateCacheNameFromHash(swContent) {
|
|
33
|
+
const hash = createHash("sha256").update(swContent).digest("hex").slice(0, 12);
|
|
34
|
+
return `sw-cache-${hash}`;
|
|
35
|
+
}
|
|
36
|
+
function applyReplacements(sw, config, assetsToCache) {
|
|
37
|
+
const { serviceWorker } = config.features;
|
|
38
|
+
const { features } = config;
|
|
39
|
+
sw = sw.replace("// [[ASSETS_LIST]]", `ASSETS_TO_CACHE = ${JSON.stringify(assetsToCache, null, 2)}`);
|
|
40
|
+
sw = sw.replace("// [[AUTO_SKIP_WAITING]]", `const AUTO_SKIP_WAITING = ${serviceWorker.autoActivate};`);
|
|
41
|
+
sw = sw.replace("// [[FETCH_HANDLER]]", generateFetchHandler(serviceWorker, features.tagInvalidation));
|
|
42
|
+
sw = sw.replace("// [[ACTIVATE_HANDLER]]", generateActivateHandler(serviceWorker.clearRuntimeOnUpdate));
|
|
43
|
+
sw = sw.replace("// [[INSTALL_HANDLER]]", generateInstallHandler());
|
|
44
|
+
sw = sw.replace("// [[MESSAGE_HANDLER]]", generateMessageHandler(features.tagInvalidation, features.auth.enabled));
|
|
45
|
+
sw = features.tagInvalidation
|
|
46
|
+
? sw.replace("// [[TAG_MANAGEMENT]]", generateTagManagement())
|
|
47
|
+
: sw.replace("// [[TAG_MANAGEMENT]]", "");
|
|
48
|
+
return sw;
|
|
49
|
+
}
|
|
31
50
|
export function assembleSW(config, version, projectRoot) {
|
|
32
|
-
const { serviceWorker
|
|
51
|
+
const { serviceWorker } = config.features;
|
|
52
|
+
const { features } = config;
|
|
33
53
|
const outputDir = config.build?.outputDir || "dist";
|
|
34
54
|
const swFilename = config.build?.swFilename || "sw";
|
|
55
|
+
const versionEnabled = serviceWorker.version.enabled;
|
|
35
56
|
const fallback = ["/index.html"];
|
|
36
57
|
if (features.pwa.enabled)
|
|
37
58
|
fallback.push("/manifest.json");
|
|
@@ -39,28 +60,28 @@ export function assembleSW(config, version, projectRoot) {
|
|
|
39
60
|
if (projectRoot) {
|
|
40
61
|
const distPath = join(projectRoot, outputDir);
|
|
41
62
|
const allAssets = collectAssets(distPath, distPath);
|
|
42
|
-
const swFile = `${swFilename}-v${version}.js`;
|
|
63
|
+
const swFile = versionEnabled ? `${swFilename}-v${version}.js` : `${swFilename}.js`;
|
|
43
64
|
for (const a of allAssets) {
|
|
44
65
|
if (a !== `/${swFile}` && a !== "/version.json")
|
|
45
66
|
scanned.push(a);
|
|
46
67
|
}
|
|
47
68
|
}
|
|
48
69
|
const assetsToCache = scanned.length > 0 ? [...new Set([...fallback, ...scanned])] : fallback;
|
|
70
|
+
const formattedAssets = assetsToCache.map((url) => ({ url, options: {} }));
|
|
49
71
|
let sw = getDefaultTemplate();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
sw = sw.replace("// [[ACTIVATE_HANDLER]]", generateActivateHandler(serviceWorker.clearRuntimeOnUpdate));
|
|
55
|
-
sw = sw.replace("// [[INSTALL_HANDLER]]", generateInstallHandler());
|
|
56
|
-
sw = sw.replace("// [[MESSAGE_HANDLER]]", generateMessageHandler(features.tagInvalidation, features.auth.enabled));
|
|
57
|
-
if (features.tagInvalidation) {
|
|
58
|
-
sw = sw.replace("// [[TAG_MANAGEMENT]]", generateTagManagement());
|
|
72
|
+
if (versionEnabled) {
|
|
73
|
+
sw = sw.replace("// [[CACHE_NAME]]", `CACHE_NAME = 'sw-v${version}'`);
|
|
74
|
+
sw = applyReplacements(sw, config, formattedAssets);
|
|
75
|
+
sw = `${generateConfigHeader(config, version)}\n\n${sw}`;
|
|
59
76
|
}
|
|
60
77
|
else {
|
|
61
|
-
|
|
78
|
+
const sentinel = "SW_CACHE_SENTINEL";
|
|
79
|
+
sw = sw.replace("// [[CACHE_NAME]]", `CACHE_NAME = '${sentinel}'`);
|
|
80
|
+
sw = applyReplacements(sw, config, formattedAssets);
|
|
81
|
+
const cacheName = generateCacheNameFromHash(sw);
|
|
82
|
+
sw = sw.replace(sentinel, cacheName);
|
|
83
|
+
sw = `${generateConfigHeader(config, version)}\n\n${sw}`;
|
|
62
84
|
}
|
|
63
|
-
sw = `${generateConfigHeader(config, version)}\n\n${sw}`;
|
|
64
85
|
if (features.backgroundSync) {
|
|
65
86
|
const authType = features.auth.enabled ? features.auth.type : undefined;
|
|
66
87
|
sw += `\n\n${generateBackgroundSyncHandler(authType)}`;
|