[dotcom] Delete service worker, cache tldraw assets (#2552)

A few things happening here

- Delete our service worker. Turns out that a couple of years back
browsers decided that a service worker is no longer required for a PWA
so you can just have the manifest and still install on the user's
device.
- Cache tldraw's assets as part of the dotcom vite asset pipeline. This
allows them to participate in the asset coalescing (preserving old
versions of asset files so old clients don't stop working when you
deploy new versions of things, see
https://github.com/tldraw/brivate/pull/3132 for more context).
- Add a new 'imports.vite.js' file to the assets package, because we
import a bunch of json translation files, and vite imports .json files
as parsed json objects instead of string urls, and there's no good way
to tell it not to. Even if there was we wouldn't want to impose that
config on our users. So another way to tell vite to load any asset as a
url string is to append `?url` to the end of the import path. That's
what this file does.

closes [#2486](https://github.com/tldraw/tldraw/issues/2486)

### Change Type

- [x] `minor` — New feature


[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version

### Release Notes

- Fix 'could not load assets' error that we often see on tldraw.com
after a deploy
This commit is contained in:
David Sheldrick 2024-01-19 15:31:01 +00:00 committed by GitHub
parent be927df935
commit ade38247d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 581 additions and 704 deletions

View file

@ -13,7 +13,6 @@
/out/
# PWA build artifacts
/public/*.js
/dev-dist
# production

View file

@ -44,7 +44,6 @@
"fast-glob": "^3.3.1",
"lazyrepo": "0.0.0-alpha.27",
"vite": "^5.0.0",
"vite-plugin-pwa": "^0.17.0",
"ws": "^8.16.0"
},
"jest": {

View file

@ -0,0 +1,38 @@
{
"name": "tldraw",
"short_name": "tldraw",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"lang": "en",
"scope": "/",
"description": "a very good free whiteboard",
"icons": [
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/android-chrome-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/android-chrome-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
}
],
"theme_color": "#ffffff",
"orientation": "any"
}

19
apps/dotcom/public/sw.js Normal file
View file

@ -0,0 +1,19 @@
// Self-destroying service worker
// Taken from https://www.benjaminrancourt.ca/how-to-remove-a-service-worker/
// Inspired from https://github.com/NekR/self-destroying-sw
self.addEventListener("install", (event) => {
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
self.registration
.unregister()
.then(() => self.clients.matchAll())
.then((clients) => {
clients.forEach((client) => {
if (client.url && "navigate" in client) {
client.navigate(client.url);
}
});
});
});

View file

@ -1,8 +1,7 @@
import { Editor, Tldraw } from '@tldraw/tldraw'
import { useCallback, useEffect } from 'react'
import { useCallback } from 'react'
import { assetUrls } from '../utils/assetUrls'
import { createAssetFromUrl } from '../utils/createAssetFromUrl'
import { isPreviewEnv } from '../utils/env'
import { linksUiOverrides } from '../utils/links'
import { DebugMenuItems } from '../utils/migration/DebugMenuItems'
import { LocalMigration } from '../utils/migration/LocalMigration'
@ -14,8 +13,6 @@ import { ShareMenu } from './ShareMenu'
import { SneakyOnDropOverride } from './SneakyOnDropOverride'
import { ThemeUpdater } from './ThemeUpdater/ThemeUpdater'
const TLDRAW_REDIRECTED_TO_SIGN_IN = 'tldraw-redirected-to-sign-in'
export function LocalEditor() {
const handleUiEvent = useHandleUiEvents()
const sharingUiOverrides = useSharing({ isMultiplayer: false })
@ -25,18 +22,6 @@ export function LocalEditor() {
editor.registerExternalAssetHandler('url', createAssetFromUrl)
}, [])
// Redirect to sign in if in preview mode
useEffect(() => {
if (isPreviewEnv) {
const alreadyRedirected = localStorage.getItem(TLDRAW_REDIRECTED_TO_SIGN_IN)
// We only want to redirect once so that we can still test the editor
if (alreadyRedirected && alreadyRedirected === 'true') return
localStorage.setItem(TLDRAW_REDIRECTED_TO_SIGN_IN, 'true')
const url = new URL(window.location.href)
window.location.assign(`${url.origin}/sign-in`)
}
}, [])
return (
<div className="tldraw__editor">
<Tldraw

View file

@ -80,3 +80,15 @@ createRoot(document.getElementById('root')!).render(
<VercelAnalytics debug={false} />
</HelmetProvider>
)
try {
// we have a dummy service worker that unregisters itself immediately
// this was needed to remove the service worker we used to have from the cache
// we can remove this if we ever need a service worker again, or if enough time passes that
// anybody returning to tldraw.com should not have a service worker running
navigator.serviceWorker.register('/sw.js', {
scope: '/',
})
} catch (e) {
// ignore
}

View file

@ -1,21 +1,7 @@
// eslint-disable-next-line import/no-internal-modules
import type { AssetUrls } from '@tldraw/assets/types'
// eslint-disable-next-line import/no-internal-modules
import { getAssetUrlsByMetaUrl } from '@tldraw/assets/urls'
import { isProductionEnv } from './env'
import { getAssetUrlsByImport } from '@tldraw/assets/imports.vite'
const _assetUrls = getAssetUrlsByMetaUrl()
export const assetUrls: AssetUrls = isProductionEnv
? _assetUrls
: // let's try out shantell sans 'informal' style for a bit on staging/dev
{
..._assetUrls,
fonts: {
..._assetUrls.fonts,
draw: '/Shantell_Sans-Tldrawish.woff2',
},
}
export const assetUrls = getAssetUrlsByImport()
let didPreloadIcons = false
async function preloadIcons() {

View file

@ -1,7 +1,6 @@
import react from '@vitejs/plugin-react-swc'
import { config } from 'dotenv'
import { defineConfig } from 'vite'
import { VitePWA, VitePWAOptions } from 'vite-plugin-pwa'
config({
path: './.env.local',
@ -11,57 +10,9 @@ export const getMultiplayerServerURL = () => {
return process.env.MULTIPLAYER_SERVER?.replace(/^ws/, 'http') ?? 'http://127.0.0.1:8787'
}
const pwaConfig: Partial<VitePWAOptions> = {
registerType: 'autoUpdate',
// Make sure the service worker doesn't try to handle API requests
workbox: {
navigateFallbackDenylist: [/^\/api/],
runtimeCaching: [{ handler: 'NetworkFirst', urlPattern: /\/.*/ }],
},
// Uncomment this to test the PWA install flow locally
// devOptions: { enabled: true },
manifest: {
name: 'tldraw',
short_name: 'tldraw',
description: 'a very good free whiteboard',
icons: [
{
src: '/android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any',
},
{
src: '/android-chrome-maskable-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable',
},
{
src: '/android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png',
purpose: 'any',
},
{
src: '/android-chrome-maskable-192x192.png',
sizes: '192x192',
type: 'image/png',
purpose: 'any maskable',
},
],
theme_color: '#ffffff',
background_color: '#ffffff',
start_url: '/',
display: 'standalone',
orientation: 'any',
},
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react({ tsDecorators: true }), VitePWA(pwaConfig)],
plugins: [react({ tsDecorators: true })],
publicDir: './public',
build: {
// output source maps to .map files and include //sourceMappingURL comments in JavaScript files