@julien-lin/universal-pwa-core 1.3.0 → 1.3.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/dist/index.cjs +121 -0
- package/dist/index.js +121 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1290,6 +1290,7 @@ function injectMetaTags(htmlContent, options = {}) {
|
|
|
1290
1290
|
const escapedSwPath = escapeJavaScriptString(swPath);
|
|
1291
1291
|
const swScript = `
|
|
1292
1292
|
<script>
|
|
1293
|
+
// Service Worker Registration
|
|
1293
1294
|
if ('serviceWorker' in navigator) {
|
|
1294
1295
|
window.addEventListener('load', () => {
|
|
1295
1296
|
navigator.serviceWorker.register(${escapedSwPath})
|
|
@@ -1297,6 +1298,67 @@ if ('serviceWorker' in navigator) {
|
|
|
1297
1298
|
.catch((error) => console.error('SW registration failed:', error));
|
|
1298
1299
|
});
|
|
1299
1300
|
}
|
|
1301
|
+
|
|
1302
|
+
// PWA Install Handler
|
|
1303
|
+
let deferredPrompt = null;
|
|
1304
|
+
let isInstalled = false;
|
|
1305
|
+
|
|
1306
|
+
// Check if app is already installed
|
|
1307
|
+
if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
|
|
1308
|
+
isInstalled = true;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// Listen for beforeinstallprompt event
|
|
1312
|
+
window.addEventListener('beforeinstallprompt', (e) => {
|
|
1313
|
+
// Prevent the mini-infobar from appearing on mobile
|
|
1314
|
+
e.preventDefault();
|
|
1315
|
+
// Stash the event so it can be triggered later
|
|
1316
|
+
deferredPrompt = e;
|
|
1317
|
+
// Update UI to notify the user they can install the PWA
|
|
1318
|
+
window.dispatchEvent(new CustomEvent('pwa-installable', { detail: { installable: true } }));
|
|
1319
|
+
console.log('PWA installable: beforeinstallprompt event captured');
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
// Listen for app installed event
|
|
1323
|
+
window.addEventListener('appinstalled', () => {
|
|
1324
|
+
isInstalled = true;
|
|
1325
|
+
deferredPrompt = null;
|
|
1326
|
+
window.dispatchEvent(new CustomEvent('pwa-installed', { detail: { installed: true } }));
|
|
1327
|
+
console.log('PWA installed successfully');
|
|
1328
|
+
});
|
|
1329
|
+
|
|
1330
|
+
// Expose global install function
|
|
1331
|
+
window.installPWA = function() {
|
|
1332
|
+
if (!deferredPrompt) {
|
|
1333
|
+
console.warn('PWA install prompt not available. App may already be installed or not installable.');
|
|
1334
|
+
return Promise.reject(new Error('PWA install prompt not available'));
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
// Show the install prompt
|
|
1338
|
+
deferredPrompt.prompt();
|
|
1339
|
+
|
|
1340
|
+
// Wait for the user to respond to the prompt
|
|
1341
|
+
return deferredPrompt.userChoice.then((choiceResult) => {
|
|
1342
|
+
if (choiceResult.outcome === 'accepted') {
|
|
1343
|
+
console.log('User accepted the install prompt');
|
|
1344
|
+
} else {
|
|
1345
|
+
console.log('User dismissed the install prompt');
|
|
1346
|
+
}
|
|
1347
|
+
deferredPrompt = null;
|
|
1348
|
+
window.dispatchEvent(new CustomEvent('pwa-install-choice', { detail: { outcome: choiceResult.outcome } }));
|
|
1349
|
+
return choiceResult;
|
|
1350
|
+
});
|
|
1351
|
+
};
|
|
1352
|
+
|
|
1353
|
+
// Expose global check function
|
|
1354
|
+
window.isPWAInstalled = function() {
|
|
1355
|
+
return isInstalled || window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
|
|
1356
|
+
};
|
|
1357
|
+
|
|
1358
|
+
// Expose global check if installable
|
|
1359
|
+
window.isPWAInstallable = function() {
|
|
1360
|
+
return deferredPrompt !== null;
|
|
1361
|
+
};
|
|
1300
1362
|
</script>`;
|
|
1301
1363
|
if (modifiedHtml.includes("</body>")) {
|
|
1302
1364
|
modifiedHtml = modifiedHtml.replace("</body>", `${swScript}
|
|
@@ -1305,7 +1367,66 @@ if ('serviceWorker' in navigator) {
|
|
|
1305
1367
|
modifiedHtml = `${modifiedHtml}${swScript}`;
|
|
1306
1368
|
}
|
|
1307
1369
|
result.injected.push("Service Worker registration script");
|
|
1370
|
+
result.injected.push("PWA install handler script");
|
|
1308
1371
|
} else {
|
|
1372
|
+
if (!htmlContent.includes("beforeinstallprompt") && !htmlContent.includes("window.installPWA")) {
|
|
1373
|
+
const installScript = `
|
|
1374
|
+
<script>
|
|
1375
|
+
// PWA Install Handler
|
|
1376
|
+
let deferredPrompt = null;
|
|
1377
|
+
let isInstalled = false;
|
|
1378
|
+
|
|
1379
|
+
// Check if app is already installed
|
|
1380
|
+
if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
|
|
1381
|
+
isInstalled = true;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// Listen for beforeinstallprompt event
|
|
1385
|
+
window.addEventListener('beforeinstallprompt', (e) => {
|
|
1386
|
+
e.preventDefault();
|
|
1387
|
+
deferredPrompt = e;
|
|
1388
|
+
window.dispatchEvent(new CustomEvent('pwa-installable', { detail: { installable: true } }));
|
|
1389
|
+
console.log('PWA installable: beforeinstallprompt event captured');
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
window.addEventListener('appinstalled', () => {
|
|
1393
|
+
isInstalled = true;
|
|
1394
|
+
deferredPrompt = null;
|
|
1395
|
+
window.dispatchEvent(new CustomEvent('pwa-installed', { detail: { installed: true } }));
|
|
1396
|
+
console.log('PWA installed successfully');
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
window.installPWA = function() {
|
|
1400
|
+
if (!deferredPrompt) {
|
|
1401
|
+
console.warn('PWA install prompt not available.');
|
|
1402
|
+
return Promise.reject(new Error('PWA install prompt not available'));
|
|
1403
|
+
}
|
|
1404
|
+
deferredPrompt.prompt();
|
|
1405
|
+
return deferredPrompt.userChoice.then((choiceResult) => {
|
|
1406
|
+
deferredPrompt = null;
|
|
1407
|
+
window.dispatchEvent(new CustomEvent('pwa-install-choice', { detail: { outcome: choiceResult.outcome } }));
|
|
1408
|
+
return choiceResult;
|
|
1409
|
+
});
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
window.isPWAInstalled = function() {
|
|
1413
|
+
return isInstalled || window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
|
|
1414
|
+
};
|
|
1415
|
+
|
|
1416
|
+
window.isPWAInstallable = function() {
|
|
1417
|
+
return deferredPrompt !== null;
|
|
1418
|
+
};
|
|
1419
|
+
</script>`;
|
|
1420
|
+
if (modifiedHtml.includes("</body>")) {
|
|
1421
|
+
modifiedHtml = modifiedHtml.replace("</body>", `${installScript}
|
|
1422
|
+
</body>`);
|
|
1423
|
+
} else {
|
|
1424
|
+
modifiedHtml = `${modifiedHtml}${installScript}`;
|
|
1425
|
+
}
|
|
1426
|
+
result.injected.push("PWA install handler script");
|
|
1427
|
+
} else {
|
|
1428
|
+
result.skipped.push("PWA install handler (already exists)");
|
|
1429
|
+
}
|
|
1309
1430
|
result.skipped.push("Service Worker registration (already exists)");
|
|
1310
1431
|
}
|
|
1311
1432
|
}
|
package/dist/index.js
CHANGED
|
@@ -1224,6 +1224,7 @@ function injectMetaTags(htmlContent, options = {}) {
|
|
|
1224
1224
|
const escapedSwPath = escapeJavaScriptString(swPath);
|
|
1225
1225
|
const swScript = `
|
|
1226
1226
|
<script>
|
|
1227
|
+
// Service Worker Registration
|
|
1227
1228
|
if ('serviceWorker' in navigator) {
|
|
1228
1229
|
window.addEventListener('load', () => {
|
|
1229
1230
|
navigator.serviceWorker.register(${escapedSwPath})
|
|
@@ -1231,6 +1232,67 @@ if ('serviceWorker' in navigator) {
|
|
|
1231
1232
|
.catch((error) => console.error('SW registration failed:', error));
|
|
1232
1233
|
});
|
|
1233
1234
|
}
|
|
1235
|
+
|
|
1236
|
+
// PWA Install Handler
|
|
1237
|
+
let deferredPrompt = null;
|
|
1238
|
+
let isInstalled = false;
|
|
1239
|
+
|
|
1240
|
+
// Check if app is already installed
|
|
1241
|
+
if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
|
|
1242
|
+
isInstalled = true;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// Listen for beforeinstallprompt event
|
|
1246
|
+
window.addEventListener('beforeinstallprompt', (e) => {
|
|
1247
|
+
// Prevent the mini-infobar from appearing on mobile
|
|
1248
|
+
e.preventDefault();
|
|
1249
|
+
// Stash the event so it can be triggered later
|
|
1250
|
+
deferredPrompt = e;
|
|
1251
|
+
// Update UI to notify the user they can install the PWA
|
|
1252
|
+
window.dispatchEvent(new CustomEvent('pwa-installable', { detail: { installable: true } }));
|
|
1253
|
+
console.log('PWA installable: beforeinstallprompt event captured');
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
// Listen for app installed event
|
|
1257
|
+
window.addEventListener('appinstalled', () => {
|
|
1258
|
+
isInstalled = true;
|
|
1259
|
+
deferredPrompt = null;
|
|
1260
|
+
window.dispatchEvent(new CustomEvent('pwa-installed', { detail: { installed: true } }));
|
|
1261
|
+
console.log('PWA installed successfully');
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
// Expose global install function
|
|
1265
|
+
window.installPWA = function() {
|
|
1266
|
+
if (!deferredPrompt) {
|
|
1267
|
+
console.warn('PWA install prompt not available. App may already be installed or not installable.');
|
|
1268
|
+
return Promise.reject(new Error('PWA install prompt not available'));
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// Show the install prompt
|
|
1272
|
+
deferredPrompt.prompt();
|
|
1273
|
+
|
|
1274
|
+
// Wait for the user to respond to the prompt
|
|
1275
|
+
return deferredPrompt.userChoice.then((choiceResult) => {
|
|
1276
|
+
if (choiceResult.outcome === 'accepted') {
|
|
1277
|
+
console.log('User accepted the install prompt');
|
|
1278
|
+
} else {
|
|
1279
|
+
console.log('User dismissed the install prompt');
|
|
1280
|
+
}
|
|
1281
|
+
deferredPrompt = null;
|
|
1282
|
+
window.dispatchEvent(new CustomEvent('pwa-install-choice', { detail: { outcome: choiceResult.outcome } }));
|
|
1283
|
+
return choiceResult;
|
|
1284
|
+
});
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1287
|
+
// Expose global check function
|
|
1288
|
+
window.isPWAInstalled = function() {
|
|
1289
|
+
return isInstalled || window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
|
|
1290
|
+
};
|
|
1291
|
+
|
|
1292
|
+
// Expose global check if installable
|
|
1293
|
+
window.isPWAInstallable = function() {
|
|
1294
|
+
return deferredPrompt !== null;
|
|
1295
|
+
};
|
|
1234
1296
|
</script>`;
|
|
1235
1297
|
if (modifiedHtml.includes("</body>")) {
|
|
1236
1298
|
modifiedHtml = modifiedHtml.replace("</body>", `${swScript}
|
|
@@ -1239,7 +1301,66 @@ if ('serviceWorker' in navigator) {
|
|
|
1239
1301
|
modifiedHtml = `${modifiedHtml}${swScript}`;
|
|
1240
1302
|
}
|
|
1241
1303
|
result.injected.push("Service Worker registration script");
|
|
1304
|
+
result.injected.push("PWA install handler script");
|
|
1242
1305
|
} else {
|
|
1306
|
+
if (!htmlContent.includes("beforeinstallprompt") && !htmlContent.includes("window.installPWA")) {
|
|
1307
|
+
const installScript = `
|
|
1308
|
+
<script>
|
|
1309
|
+
// PWA Install Handler
|
|
1310
|
+
let deferredPrompt = null;
|
|
1311
|
+
let isInstalled = false;
|
|
1312
|
+
|
|
1313
|
+
// Check if app is already installed
|
|
1314
|
+
if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
|
|
1315
|
+
isInstalled = true;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
// Listen for beforeinstallprompt event
|
|
1319
|
+
window.addEventListener('beforeinstallprompt', (e) => {
|
|
1320
|
+
e.preventDefault();
|
|
1321
|
+
deferredPrompt = e;
|
|
1322
|
+
window.dispatchEvent(new CustomEvent('pwa-installable', { detail: { installable: true } }));
|
|
1323
|
+
console.log('PWA installable: beforeinstallprompt event captured');
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1326
|
+
window.addEventListener('appinstalled', () => {
|
|
1327
|
+
isInstalled = true;
|
|
1328
|
+
deferredPrompt = null;
|
|
1329
|
+
window.dispatchEvent(new CustomEvent('pwa-installed', { detail: { installed: true } }));
|
|
1330
|
+
console.log('PWA installed successfully');
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
window.installPWA = function() {
|
|
1334
|
+
if (!deferredPrompt) {
|
|
1335
|
+
console.warn('PWA install prompt not available.');
|
|
1336
|
+
return Promise.reject(new Error('PWA install prompt not available'));
|
|
1337
|
+
}
|
|
1338
|
+
deferredPrompt.prompt();
|
|
1339
|
+
return deferredPrompt.userChoice.then((choiceResult) => {
|
|
1340
|
+
deferredPrompt = null;
|
|
1341
|
+
window.dispatchEvent(new CustomEvent('pwa-install-choice', { detail: { outcome: choiceResult.outcome } }));
|
|
1342
|
+
return choiceResult;
|
|
1343
|
+
});
|
|
1344
|
+
};
|
|
1345
|
+
|
|
1346
|
+
window.isPWAInstalled = function() {
|
|
1347
|
+
return isInstalled || window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
|
|
1348
|
+
};
|
|
1349
|
+
|
|
1350
|
+
window.isPWAInstallable = function() {
|
|
1351
|
+
return deferredPrompt !== null;
|
|
1352
|
+
};
|
|
1353
|
+
</script>`;
|
|
1354
|
+
if (modifiedHtml.includes("</body>")) {
|
|
1355
|
+
modifiedHtml = modifiedHtml.replace("</body>", `${installScript}
|
|
1356
|
+
</body>`);
|
|
1357
|
+
} else {
|
|
1358
|
+
modifiedHtml = `${modifiedHtml}${installScript}`;
|
|
1359
|
+
}
|
|
1360
|
+
result.injected.push("PWA install handler script");
|
|
1361
|
+
} else {
|
|
1362
|
+
result.skipped.push("PWA install handler (already exists)");
|
|
1363
|
+
}
|
|
1243
1364
|
result.skipped.push("Service Worker registration (already exists)");
|
|
1244
1365
|
}
|
|
1245
1366
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@julien-lin/universal-pwa-core",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Core engine for scanning, generation, and injection for UniversalPWA",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pwa",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"workbox-routing": "^7.4.0",
|
|
62
62
|
"workbox-strategies": "^7.4.0",
|
|
63
63
|
"zod": "^4.2.1",
|
|
64
|
-
"@julien-lin/universal-pwa-templates": "^1.3.
|
|
64
|
+
"@julien-lin/universal-pwa-templates": "^1.3.1"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@vitest/coverage-v8": "^2.1.4",
|