refactor: Colocate service worker build script with source files
Move build script from scripts/build-service-worker.js to app/javascript/service-worker/build.js to keep all service worker related code in one place. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
38232a73dc
commit
adbee23e12
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console, no-restricted-syntax, no-continue */
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
@@ -9,9 +10,10 @@ const { build } = require('vite');
|
||||
* Returns array of { url, revision } for JS/CSS files
|
||||
*/
|
||||
function generateAssetManifest() {
|
||||
// vite-plugin-ruby outputs to 'vite' in production, 'vite-dev' in development
|
||||
const manifestPaths = [
|
||||
path.resolve(__dirname, '../public/packs/.vite/manifest.json'),
|
||||
path.resolve(__dirname, '../public/vite-dev/.vite/manifest.json'),
|
||||
path.resolve(__dirname, '../../../public/vite/.vite/manifest.json'),
|
||||
path.resolve(__dirname, '../../../public/vite-dev/.vite/manifest.json'),
|
||||
];
|
||||
|
||||
let manifestPath = manifestPaths.find(p => fs.existsSync(p));
|
||||
@@ -56,29 +58,26 @@ async function buildServiceWorker() {
|
||||
|
||||
const assetOrigin = process.env.ASSET_CDN_HOST || '';
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
// In production assets are in /packs/, in development they're in /vite-dev/
|
||||
const assetPath = isProduction ? '/packs/' : '/vite-dev/';
|
||||
// vite-plugin-ruby serves from /vite/ in production, /vite-dev/ in development
|
||||
const assetPath = isProduction ? '/vite/' : '/vite-dev/';
|
||||
|
||||
console.log(`🌐 Asset origin: ${assetOrigin || '(local)'}`);
|
||||
console.log(`📁 Asset path: ${assetPath}`);
|
||||
console.log(`🏭 Environment: ${isProduction ? 'production' : 'development'}`);
|
||||
|
||||
// In development mode, just copy the push-handlers-only version
|
||||
// In development mode, just use push handlers (no caching)
|
||||
if (!isProduction) {
|
||||
console.log(
|
||||
'⚠️ Development mode: Using push notifications only (no caching)'
|
||||
);
|
||||
|
||||
const devServiceWorker = fs.readFileSync(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../app/javascript/service-worker/push-handlers-only.js'
|
||||
),
|
||||
path.resolve(__dirname, 'push-handlers.js'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.resolve(__dirname, '../public/sw.js'),
|
||||
path.resolve(__dirname, '../../../public/sw.js'),
|
||||
devServiceWorker
|
||||
);
|
||||
|
||||
@@ -103,15 +102,12 @@ async function buildServiceWorker() {
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry: path.resolve(
|
||||
__dirname,
|
||||
'../app/javascript/service-worker/sw-runtime.js'
|
||||
),
|
||||
entry: path.resolve(__dirname, 'sw-runtime.js'),
|
||||
formats: ['iife'],
|
||||
name: 'ServiceWorkerRuntime',
|
||||
fileName: () => 'sw-runtime.js',
|
||||
},
|
||||
outDir: path.resolve(__dirname, '../tmp/sw-build'),
|
||||
outDir: path.resolve(__dirname, '../../../tmp/sw-build'),
|
||||
emptyOutDir: true,
|
||||
minify: true,
|
||||
rollupOptions: {
|
||||
@@ -133,16 +129,13 @@ async function buildServiceWorker() {
|
||||
|
||||
// Read the built runtime
|
||||
const runtimeBundle = fs.readFileSync(
|
||||
path.resolve(__dirname, '../tmp/sw-build/sw-runtime.js'),
|
||||
path.resolve(__dirname, '../../../tmp/sw-build/sw-runtime.js'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
// Read the push handlers
|
||||
const pushHandlers = fs.readFileSync(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../app/javascript/service-worker/push-handlers.js'
|
||||
),
|
||||
path.resolve(__dirname, 'push-handlers.js'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
@@ -158,7 +151,7 @@ ${pushHandlers}
|
||||
|
||||
// Write to public/sw.js
|
||||
fs.writeFileSync(
|
||||
path.resolve(__dirname, '../public/sw.js'),
|
||||
path.resolve(__dirname, '../../../public/sw.js'),
|
||||
finalServiceWorker
|
||||
);
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/* Development-only service worker - Push notifications only, no caching */
|
||||
/* eslint-disable no-restricted-globals, no-console */
|
||||
/* globals clients */
|
||||
|
||||
self.addEventListener('push', event => {
|
||||
let notification = event.data && event.data.json();
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.showNotification(notification.title, {
|
||||
tag: notification.tag,
|
||||
data: {
|
||||
url: notification.url,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('notificationclick', event => {
|
||||
let notification = event.notification;
|
||||
|
||||
event.waitUntil(
|
||||
clients.matchAll({ type: 'window' }).then(windowClients => {
|
||||
let matchingWindowClients = windowClients.filter(
|
||||
client => client.url === notification.data.url
|
||||
);
|
||||
|
||||
if (matchingWindowClients.length) {
|
||||
let firstWindow = matchingWindowClients[0];
|
||||
if (firstWindow && 'focus' in firstWindow) {
|
||||
firstWindow.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (clients.openWindow) {
|
||||
clients.openWindow(notification.data.url);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -1,11 +1,7 @@
|
||||
/* eslint-disable no-restricted-globals */
|
||||
/* eslint-disable no-restricted-globals, no-console */
|
||||
/* globals clients */
|
||||
|
||||
// Push notification handler
|
||||
self.addEventListener('push', event => {
|
||||
const notification = event.data && event.data.json();
|
||||
|
||||
if (!notification) return;
|
||||
let notification = event.data && event.data.json();
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.showNotification(notification.title, {
|
||||
@@ -17,19 +13,17 @@ self.addEventListener('push', event => {
|
||||
);
|
||||
});
|
||||
|
||||
// Notification click handler
|
||||
self.addEventListener('notificationclick', event => {
|
||||
const notification = event.notification;
|
||||
notification.close();
|
||||
let notification = event.notification;
|
||||
|
||||
event.waitUntil(
|
||||
clients.matchAll({ type: 'window' }).then(windowClients => {
|
||||
const matchingWindowClients = windowClients.filter(
|
||||
let matchingWindowClients = windowClients.filter(
|
||||
client => client.url === notification.data.url
|
||||
);
|
||||
|
||||
if (matchingWindowClients.length) {
|
||||
const firstWindow = matchingWindowClients[0];
|
||||
let firstWindow = matchingWindowClients[0];
|
||||
if (firstWindow && 'focus' in firstWindow) {
|
||||
firstWindow.focus();
|
||||
return;
|
||||
|
||||
@@ -46,7 +46,7 @@ self.addEventListener('fetch', event => {
|
||||
// Assets (JS/CSS/fonts) → cache-first
|
||||
const isAsset =
|
||||
url.pathname.startsWith('/vite-dev/') ||
|
||||
url.pathname.startsWith('/packs/') ||
|
||||
url.pathname.startsWith('/vite/') ||
|
||||
url.origin === ASSET_ORIGIN ||
|
||||
/\.(js|css|woff2?|ttf|otf|eot)$/i.test(url.pathname);
|
||||
|
||||
@@ -183,7 +183,7 @@ async function cleanupStaleAssets() {
|
||||
.filter(request => {
|
||||
const url = new URL(request.url);
|
||||
const isAssetPath =
|
||||
url.pathname.startsWith('/packs/assets/') ||
|
||||
url.pathname.startsWith('/vite/assets/') ||
|
||||
url.pathname.startsWith('/vite-dev/assets/');
|
||||
// Only clean up asset files, keep other cached items (like /app shell)
|
||||
return isAssetPath && !validUrls.has(request.url);
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
"dev": "overmind start -f ./Procfile.dev",
|
||||
"ruby:prettier": "bundle exec rubocop -a",
|
||||
"build:sdk": "BUILD_MODE=library vite build",
|
||||
"build:sw": "node scripts/build-service-worker.js",
|
||||
"build:sw": "node app/javascript/service-worker/build.js",
|
||||
"prepare": "husky install",
|
||||
"size": "size-limit",
|
||||
"story:dev": "histoire dev",
|
||||
|
||||
Reference in New Issue
Block a user