Skip to content

Commit

Permalink
feat: cache sw assets with service worker (#234)
Browse files Browse the repository at this point in the history
* feat: cache sw assets with service worker

* feat: cache index.html requests

* feat: also cache cdn.jsdelivr assets

* fix: ensure old swAssets cache is cleared on install

* feat: include tachyons and ipfs-css in bundle

* fix: less duplication of css imports

* fix: dep-check on css imports
  • Loading branch information
SgtPooki authored May 14, 2024
1 parent d3c80a1 commit 20a8f32
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 18 deletions.
4 changes: 4 additions & 0 deletions .aegir.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export default {
// .jsx files aren't checked properly.
'react-dom',

// .css deps aren't checked properly.
'ipfs-css',
'tachyons',

// required by webpack
'webpack-cli',
'webpack-dev-server',
Expand Down
14 changes: 13 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@
"@multiformats/dns": "^1.0.6",
"@sgtpooki/file-type": "^1.0.1",
"debug": "^4.3.4",
"ipfs-css": "^1.4.0",
"multiformats": "^13.1.0",
"react": "^18.3.0",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"tachyons": "^4.12.0"
},
"devDependencies": {
"@babel/core": "^7.24.3",
Expand Down
2 changes: 0 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= htmlWebpackPlugin.options.title %> | <%= htmlWebpackPlugin.options.version %></title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tachyons@4.12.0/css/tachyons.min.css" integrity="sha256-MgEf5i1a74lVzhT+1R6mBbWCUeUaxC8sQTaN5GY+CoI=" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ipfs-css@1.4.0/ipfs.css" integrity="sha256-tlU/gvVvLjSbTOfSZyCzuQxY8QcmHPtJJ1oTXilA9gk=" crossorigin="anonymous">
<meta name="description" content="A static HTML page that initializes an instance of IPFS Service Worker Gateway" />
<meta name="robots" content="noindex" />
</head>
Expand Down
9 changes: 0 additions & 9 deletions src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,6 @@ form {
letter-spacing: -12px;
}

.terminal {
margin: 20px;
font-family: monospace;
font-size: 16px;
overflow: auto;
flex: 1;
word-break: break-word;
}

.cursor-disabled {
cursor: not-allowed;
}
1 change: 1 addition & 0 deletions src/pages/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { HeliaServiceWorkerCommsChannel } from '../lib/channel.js'
import { getConfig, loadConfigFromLocalStorage } from '../lib/config-db.js'
import { LOCAL_STORAGE_KEYS } from '../lib/local-storage.js'
import { getUiComponentLogger, uiLogger } from '../lib/logger.js'
import './default-page-styles.css'

const uiComponentLogger = getUiComponentLogger('config-page')
const log = uiLogger.forComponent('config-page')
Expand Down
2 changes: 2 additions & 0 deletions src/pages/default-page-styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import 'tachyons';
@import 'ipfs-css';
1 change: 1 addition & 0 deletions src/pages/helper-ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import CidRenderer from '../components/input-validator.jsx'
import { ConfigProvider } from '../context/config-context.jsx'
import { ServiceWorkerProvider } from '../context/service-worker-context.jsx'
import { LOCAL_STORAGE_KEYS } from '../lib/local-storage.js'
import './default-page-styles.css'

function HelperUi (): React.JSX.Element {
const [requestPath, setRequestPath] = useState(localStorage.getItem(LOCAL_STORAGE_KEYS.forms.requestPath) ?? '')
Expand Down
1 change: 1 addition & 0 deletions src/pages/redirect-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getSubdomainParts } from '../lib/get-subdomain-parts.js'
import { isConfigPage } from '../lib/is-config-page.js'
import { getUiComponentLogger, uiLogger } from '../lib/logger.js'
import { translateIpfsRedirectUrl } from '../lib/translate-ipfs-redirect-url.js'
import './default-page-styles.css'

const uiComponentLogger = getUiComponentLogger('redirect-page')
const log = uiLogger.forComponent('redirect-page')
Expand Down
2 changes: 1 addition & 1 deletion src/pages/redirects-interstitial.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect } from 'react'
import { findOriginIsolationRedirect } from '../lib/path-or-subdomain.js'
import { translateIpfsRedirectUrl } from '../lib/translate-ipfs-redirect-url.js'
import RedirectPage from './redirect-page'
import RedirectPage from './redirect-page.jsx'

/**
* This page is only used to capture the ?helia-sw=/ip[fn]s/blah query parameter that
Expand Down
44 changes: 40 additions & 4 deletions src/sw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ const log = swLogger.forComponent('main')
const CACHE_VERSION = 1
const CURRENT_CACHES = Object.freeze({
mutable: `mutable-cache-v${CACHE_VERSION}`,
immutable: `immutable-cache-v${CACHE_VERSION}`
immutable: `immutable-cache-v${CACHE_VERSION}`,
swAssets: `sw-assets-v${CACHE_VERSION}`
})
let verifiedFetch: VerifiedFetch
const channel = new HeliaServiceWorkerCommsChannel('SW', swLogger)
Expand All @@ -123,6 +124,7 @@ self.addEventListener('install', (event) => {
// 👇 When a new version of the SW is installed, activate immediately
void self.skipWaiting()
event.waitUntil(addInstallTimestampToConfig())
event.waitUntil(clearSwAssetCache())
})

self.addEventListener('activate', (event) => {
Expand Down Expand Up @@ -199,8 +201,23 @@ async function requestRouting (event: FetchEvent, url: URL): Promise<boolean> {
} else if (isDeregisterRequest(event.request.url)) {
event.waitUntil(deregister(event))
return false
} else if (isConfigPageRequest(url) || isSwAssetRequest(event)) {
log.trace('config page or sw-asset request, ignoring ', event.request.url)
} else if (isConfigPageRequest(url)) {
log.trace('config page request, ignoring ', event.request.url)
return false
} else if (isSwAssetRequest(event)) {
log.trace('sw-asset request, returning cached response ', event.request.url)
/**
* Return the asset from the cache if it exists, otherwise fetch it.
*/
event.respondWith(caches.open(CURRENT_CACHES.swAssets).then(async (cache) => {
const cachedResponse = await cache.match(event.request)
if (cachedResponse != null) {
return cachedResponse
}
const response = await fetch(event.request)
await cache.put(event.request, response.clone())
return response
}))
return false
} else if (!isValidRequestForSW(event)) {
log.trace('not a valid request for helia-sw, ignoring ', event.request.url)
Expand Down Expand Up @@ -285,7 +302,12 @@ function isAggregateError (err: unknown): err is AggregateError {

function isSwAssetRequest (event: FetchEvent): boolean {
const isActualSwAsset = /^.+\/(?:ipfs-sw-).+$/.test(event.request.url)
return isActualSwAsset
// if path is not set, then it's a request for index.html which we should consider a sw asset
const url = new URL(event.request.url)
// but only if it's not a subdomain request (root index.html should not be returned for subdomains)
const isIndexHtmlRequest = url.pathname === '/' && !isSubdomainRequest(event)

return isActualSwAsset || isIndexHtmlRequest
}

/**
Expand Down Expand Up @@ -629,3 +651,17 @@ async function addInstallTimestampToConfig (): Promise<void> {
log.error('addInstallTimestampToConfig error: ', e)
}
}

/**
* To be called on 'install' sw event. This will clear out the old swAssets cache,
* which is used for storing the service worker's css,js, and html assets.
*/
async function clearSwAssetCache (): Promise<void> {
// clear out old swAssets cache
const cacheName = CURRENT_CACHES.swAssets
const cache = await caches.open(cacheName)
const keys = await cache.keys()
for (const request of keys) {
await cache.delete(request)
}
}

0 comments on commit 20a8f32

Please sign in to comment.