Skip to content

Instantly share code, notes, and snippets.

@kulterryan
Created August 17, 2025 17:43
Show Gist options
  • Save kulterryan/4ecc4d932a28ea17d03fff41cf86ba62 to your computer and use it in GitHub Desktop.
Save kulterryan/4ecc4d932a28ea17d03fff41cf86ba62 to your computer and use it in GitHub Desktop.
#!/usr/bin/env node
import { generateSW } from 'workbox-build'
import { copyFile, mkdir } from 'node:fs/promises'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
async function main() {
const clientDist = resolve(__dirname, '..', '.tanstack', 'start', 'build', 'client-dist')
const outPublic = resolve(__dirname, '..', '.output', 'public')
const { count, warnings, size } = await generateSW({
swDest: resolve(clientDist, 'sw.js'),
globDirectory: clientDist,
globPatterns: [
'**/*.{js,css,html,svg,png,ico,webmanifest,json}',
'offline.html'
],
skipWaiting: true,
clientsClaim: true,
navigateFallback: '/offline.html',
navigateFallbackAllowlist: [/^\/$/ , /^(?!\/api\/)/],
runtimeCaching: [
{
urlPattern: ({ url }) => /\/api\//.test(url.pathname),
handler: 'NetworkFirst',
options: {
cacheName: 'api',
networkTimeoutSeconds: 10,
expiration: { maxEntries: 50, maxAgeSeconds: 60 * 5 },
cacheableResponse: { statuses: [0, 200] }
}
},
{
// Images
urlPattern: ({ request }) => request.destination === 'image',
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'images',
expiration: { maxEntries: 80, maxAgeSeconds: 60 * 60 * 24 * 30 }
}
},
{
// Google Fonts stylesheets & font files
urlPattern: ({ url }) => /^(https:\/\/(fonts\.googleapis\.com|fonts\.gstatic\.com))\//.test(url.href),
handler: 'CacheFirst',
options: {
cacheName: 'google-fonts',
expiration: { maxEntries: 30, maxAgeSeconds: 60 * 60 * 24 * 365 }
}
}
]
})
if (warnings.length) {
console.warn('[workbox] warnings:', warnings)
}
console.log(`[workbox] generated sw.js with ${count} precached files (${(size/1024).toFixed(1)} KiB)`)
// ensure public output dir exists and copy sw.js
await mkdir(outPublic, { recursive: true })
await copyFile(resolve(clientDist, 'sw.js'), resolve(outPublic, 'sw.js'))
console.log('[workbox] copied sw.js to .output/public')
}
main().catch(e => {
console.error(e)
process.exit(1)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment