-
Notifications
You must be signed in to change notification settings - Fork 274
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Offline support #1483
Offline support #1483
Conversation
We will need to rebuild this prototype to support:
We won't cache WP assets that are currently loaded from the server. Instead, we will load them all during boot. |
packages/php-wasm/web-service-worker/src/initialize-service-worker.ts
Outdated
Show resolved
Hide resolved
@bgrgicak I meant this file: All the fetch requests to playground.wordpress.net go through that code path. The wordpress-playground/packages/php-wasm/web-service-worker/src/initialize-service-worker.ts Line 20 in 172d78f
A downside of |
This. From the documented motivations in the Service Worker spec:
Service Workers are intentionally positioned to provide such a caching layer. I often wish the web platform was not so complicated and generally resent the complexity introduced by Service Workers. But this seems like a place we can appreciate them. :) (Actually, Playground as a whole has been a wonderful demonstration of the merits of Service Workers) |
@brandonpayton @adamziel offline support should work now in this PR (without preloading and busting). I have an issue locally where Playground crashes on reload because the Vite server isn't available while offline, but I'm not sure how to resolve it. If you have some time, please take a look at it. To recreate the issue:
If you don't see the error enable Persistent log in the JS console. |
@@ -0,0 +1,68 @@ | |||
import { areCacheKeysForSameFile, isValidFile } from '../lib/fetch-caching'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is isValidFile()
defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this! I took a look and left some questions. There are likely things I don't properly understand yet.
).toBe(true); | ||
}); | ||
|
||
it('Shoud return true if the cache keys are for the same file', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: "Shoud"
return await cache.then((c) => c.match(key)); | ||
}; | ||
|
||
const validHostnames = ['playground.wordpress.net', '127.0.0.1', 'localhost']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to validate hostnames? What if people want to host their own Playground?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't but I didn't want to cache their party scripts like Google Analytics.
But we could just cache everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, I had no idea Service Workers also receive fetch events for requests to third party resources. But it appears to be true.
It looks like fetchEvent.currentTarget.registration.scope
is available, so we could filter based on that rather than a specific set of hostnames.
fetchEvent.currentTarget
points to the ServiceWorkerGlobalScope. ServiceWorkerGlobalScope exposes the ServiceWorkerRegistration as its registration property, and ServiceWorkerRegistration has the scope property.
|
||
const precachedResources: RequestInfo[] = [ | ||
'/website-server/', | ||
'/website-server/index.html', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we precaching these paths specifically? How do we adjust in production where there is no /website-server/
directory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These files are required for Playground to load when offline. They aren't fetched by the service worker so they need to be preached in this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These files are required for Playground to load when offline.
These files don't appear to exist at all on the production site.
They aren't fetched by the service worker so they need to be preached in this way.
Do you mean on initial load before the service worker can be registered?
@@ -22,28 +33,28 @@ export function initializeServiceWorker(config: ServiceWorkerConfiguration) { | |||
|
|||
// Don't handle requests to the service worker script itself. | |||
if (url.pathname.startsWith(self.location.pathname)) { | |||
return; | |||
// return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this commented out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This return breaks caching. I'm still working on the PR so I didn't do anything with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to cache the service worker script?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This return breaks caching. I'm still working on the PR so I didn't do anything with it.
Do we want to cache the service worker script?
To clarify the question: It looks like this only stops caching of the service worker script. Is that correct, and if so, why do we want to cache the service worker script?
) | ||
) { | ||
// Add caching for non-scoped requests | ||
event.respondWith(cachedFetch(event.request)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are non-scoped requests typically requests for Playground web UI assets?
Would it be good for us to use a completely separate cache for app resources so we can easily flush the cache for those when we detect a new version of the Playground web UI?
We could consider things like that more in a future iteration, but if we do that, I think it will be easy to forget our previous caching scheme(s) and leave cruft in the cache from earlier versions. Maybe that is OK... I'm not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are non-scoped requests typically requests for Playground web UI assets?
They request Playground web UI assets, WP remote assets, WP zip, PHP wasm, and JS.
Would it be good for us to use a completely separate cache for app resources so we can easily flush the cache for those when we detect a new version of the Playground web UI?
Yes, but I would prefer to get this out first and improve later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but I would prefer to get this out first and improve later.
👍 works for me.
event.respondWith(responsePromise); | ||
const response = handleRequest(event); | ||
if (response) { | ||
event.respondWith(response); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these scoped requests for things like WP assets? Does that mean we are not caching them? Or do the de-scoped requests made by handleRequest()
become new fetch
events that are handled above.
In my current mental model, fetches by the Service Worker do not trigger additional fetch
events, but that might just be a poor assumption on my part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this helpes, but here is how requests work.
All requests in the app go to this listener, no matter if it's a remote request or an internal Playground request.
Remote requests should be cached to add offline support. These are files like the WP zip and remote static assets.
There are also third-party remote requests like Google analytics, that we don't cache for now.
Internal Playground requests are the same as scoped requests. This is where we reroute the request to go to PHP-wasm instead of going to a fetch. This would be a WordPress page load, an asset that exists inside the Playground filesystem like style.css, or a PHP script execution request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for explaining a bit. In my mental model yesterday, I'd forgotten that many WordPress files would be available locally.
Note: The following question may be irrelevant if you are planning to proactively cache remote WP assets separately.
For scoped requests for remote static assets that were eliminated from the WordPress build, won't those skip caching because:
- scoped requests are not cached
- scoped requests for remote WP assets are fetched by the Service Worker
- fetch() by the Service Worker does not trigger subsequent
fetch
events (in my understanding and testing)
?
I made some progress on caching static assets today, but need to get back to this PR tomorrow. The Vite server is preventing caching from working. When online it messes with MIME types , when online it prevents Playground from running because the socket connection fails. |
If needed, we can just skip the Vite server for now to test single website builds: npm run build:website
php -S localhost:9999 -t dist/packages/playground/wasm-wordpress-net The site is then served from: There are some errors when building the website under this offline branch, but I am able to test a trunk website build with the above approach. It requires rebuilding the website after each change, but it is at least an alternative to the Vite server. |
I'm closing this PR, all of the changes are moved to #1535 |
## Motivation for the change, related issues To achieve [offline support](#1483) we need to download all WordPress files into the Playground filesystem. Today Playground downloads WordPress partially on boot and downloads assets as they are needed using fetch requests. This PR downloads all assets into the Playground filesystem without blocking the boot process. ## Implementation details Zip files are generated using the WordPress build script and are accessible in the `/wp-VERSION/` folder like any other assets. The download is triggered after WordPress is installed. The fetch is async and won't block the boot process. In case some of the assets are needed immediately on boot they will still be downloaded using fetch to ensure they are available to WordPress. Once the assets are downloaded they are stored in the Playground filesystem and will be served from there. ## Testing Instructions (or ideally a Blueprint) - Run `npx nx bundle-wordpress:nightly playground-wordpress-builds` - Confirm that a `packages/playground/wordpress-builds/public/wp-nightly/wordpress-static.zip` file was created - [Load Playground using this blueprint](http://127.0.0.1:5400/website-server/?php=8.0&wp=nightly) - In network tools see that the `wordpress-static.zip` file was downloaded - Open `/wp-admin/` - In network tools confirm that static assets like _thickbox.js_ were loaded from the service worker instead of a fetch
## Motivation for the change, related issues To achieve [offline support](#1483) we need to download all WordPress files into the Playground filesystem. Today Playground downloads WordPress partially on boot and downloads assets as they are needed using fetch requests. This PR downloads all assets into the Playground filesystem without blocking the boot process. ## Implementation details Zip files are generated using the WordPress build script and are accessible in the `/wp-VERSION/` folder like any other assets. The download is triggered after WordPress is installed. The fetch is async and won't block the boot process. In case some of the assets are needed immediately on boot they will still be downloaded using fetch to ensure they are available to WordPress. Once the assets are downloaded they are stored in the Playground filesystem and will be served from there. ## Testing Instructions (or ideally a Blueprint) - Run `npx nx bundle-wordpress:nightly playground-wordpress-builds` - Confirm that a `packages/playground/wordpress-builds/public/wp-nightly/wordpress-static.zip` file was created - [Load Playground using this blueprint](http://127.0.0.1:5400/website-server/?php=8.0&wp=nightly) - In network tools see that the `wordpress-static.zip` file was downloaded - Open `/wp-admin/` - In network tools confirm that static assets like _thickbox.js_ were loaded from the service worker instead of a fetch --------- Co-authored-by: Adam Zieliński <[email protected]>
Motivation for the change, related issues
Most of Playgrounds load time is waiting for files to download.
If the user reloads Playground they are waiting for the same files to download.
This PR improves Playground load time by caching WP, PHP, and the SQLite plugin in the browser.
Implementation details
The PR uses browser cache storage to cache fetch responses and uses the request URL as the cache key.
Because WP, PHP, and the SQLite plugin change URLs when they are rebuilt the cache will be busted and the correct version will be downloaded.
Testing Instructions (or ideally a Blueprint)
wp-6.5.zip
was downloade