From a986d69c288a9c7543d5861a8a8b29ecc08eec35 Mon Sep 17 00:00:00 2001 From: IVA2K Date: Tue, 30 Jul 2024 23:01:39 -0700 Subject: [PATCH 1/2] Enable use of env.SW_DEV in pwaConfiguration.devOptions.enabled * Make `pnpm dev` not use ServiceWorker * Add package.json:scripts `pnpm dev-sw:http`, `pnpm dev-sw:https` to debug with ServiceWorker --- package.json | 6 ++++-- pwa-configuration.js | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index cbcfdc83..52076f65 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,10 @@ "svelte:prebuild": "echo RUN svelte:prebuild && rimraf .svelte-kit && rimraf build && svelte-kit sync && tsx scripts/assets-copy.ts", "svelte:predev": "echo RUN svelte:predev && rimraf dev-dist && svelte-kit sync && tsx scripts/assets-copy.ts", "dev": " pnpm dev:http", - "dev:http": " echo RUN dev:http && pnpm svelte:predev && cross-env SW_DEV=true DEBUG=vite-plugin-pwa:* NO_HTTPS=1 vite dev --port 3000", - "dev:https": " echo RUN dev:https && pnpm svelte:predev && cross-env SW_DEV=true DEBUG=vite-plugin-pwa:* vite dev --port 3000", + "dev:http": " echo RUN dev:http && pnpm svelte:predev && cross-env DEBUG=vite-plugin-pwa:* NO_HTTPS=1 vite dev --port 3000", + "dev:https": " echo RUN dev:https && pnpm svelte:predev && cross-env DEBUG=vite-plugin-pwa:* vite dev --port 3000", + "dev-sw:http": " echo RUN dev-sw:http && pnpm svelte:predev && cross-env SW_DEV=true DEBUG=vite-plugin-pwa:* NO_HTTPS=1 vite dev --port 3000", + "dev-sw:https": " echo RUN dev-sw:https && pnpm svelte:predev && cross-env SW_DEV=true DEBUG=vite-plugin-pwa:* vite dev --port 3000", "dev-claims": " echo RUN dev-claims && pnpm svelte:predev && cross-env SW_DEV=true DEBUG=vite-plugin-pwa:* SW=true CLAIMS=true vite dev --port 3000", "dev-destroy": " echo RUN dev-destroy && pnpm svelte:predev && cross-env SW_DEV=true DEBUG=vite-plugin-pwa:* SW_DESTROY=true vite dev --port 3000", "dev-claims-destroy": " echo RUN dev-claims-destroy && pnpm svelte:predev && cross-env SW_DEV=true DEBUG=vite-plugin-pwa:* SW=true SW_DESTROY=true CLAIMS=true vite dev --port 3000", diff --git a/pwa-configuration.js b/pwa-configuration.js index f48225e5..c9dc11b8 100644 --- a/pwa-configuration.js +++ b/pwa-configuration.js @@ -86,8 +86,7 @@ const pwaConfigurationFnc = async ( // default: minify: true, devOptions: { - // enabled: process.env.SW_DEV === 'true', - enabled: true, + enabled: process.env.SW_DEV === 'true', /* when using generateSW the PWA plugin will switch to classic */ type: 'module', navigateFallback: scope From 36be3ee31ce184da36da4aa9cc0acfcf24d753b1 Mon Sep 17 00:00:00 2001 From: IVA2K Date: Wed, 31 Jul 2024 00:52:09 -0700 Subject: [PATCH 2/2] Redesign siteLinks use to lazy-load images: * Sync prepSiteLinks() for SSR and pre-rendering * Parallel loading in onMount() with array of promises of loaded components --- src/lib/components/header/PureHeader.svelte | 45 ++-- src/lib/config/configUtils.ts | 281 ++++++++------------ src/routes/(demo)/+layout.svelte | 24 +- 3 files changed, 151 insertions(+), 199 deletions(-) diff --git a/src/lib/components/header/PureHeader.svelte b/src/lib/components/header/PureHeader.svelte index 7ca16381..73e14bef 100644 --- a/src/lib/components/header/PureHeader.svelte +++ b/src/lib/components/header/PureHeader.svelte @@ -2,40 +2,39 @@ import { onMount } from 'svelte'; import logo from '$lib/images/logo.svg'; import website from '$lib/config/website'; - import { prepSiteLinks } from '$lib/config/configUtils'; + import { loadSiteLinks, prepSiteLinks } from '$lib/config/configUtils'; import type { SiteLink } from '$lib/types'; const { websiteUrlBase, siteLinks } = website; export let pathname = '/'; $: path1st = '/' + (pathname ?? '').split('/')[1]; - let brandLink: SiteLink; - let headerLinks: SiteLink[] = []; + let brandLink: SiteLink = prepSiteLinks( + siteLinks, + 'brand', + 1, + /* nodeFilter */ true, + /* flatten */ true, + /* prune */ false // Allow brand without a link + )?.[0]; + let headerLinks: SiteLink[] = prepSiteLinks( + siteLinks, + 'header', + 2, + true, + /* flatten */ true, + /* prune */ true + ); onMount(async () => { /* DISABLED (see root +layout.svelte) await loadIonicPWAElements(window); */ const mypath = import.meta.url; - brandLink = ( - await prepSiteLinks( - siteLinks, - mypath, - 'brand', - 1, - /* nodeFilter */ true, - /* flatten */ true, - /* prune */ false // Allow brand without a link - ) - )?.[0]; - headerLinks = await prepSiteLinks( - siteLinks, - mypath, - 'header', - 2, - true, - /* flatten */ true, - /* prune */ true - ); + // [brandLink, ... headerLinks] = + await Promise.all([ + loadSiteLinks([brandLink], mypath)?.[0], + ...loadSiteLinks(headerLinks, mypath) + ]); console.log('DEBUG: headerLinks=%o', headerLinks); }); diff --git a/src/lib/config/configUtils.ts b/src/lib/config/configUtils.ts index 204e8cba..01a960bc 100644 --- a/src/lib/config/configUtils.ts +++ b/src/lib/config/configUtils.ts @@ -79,96 +79,90 @@ function getSiteLinkGroupItems(link: SiteLinkAny): SiteLinkAny[] | undefined { type UnwrapArray = T extends (infer U)[] ? U : T; -export async function getSiteLinksComponents( - siteLinks: SiteLink[], +export async function getSiteLinkComponent( + siteLink: SiteLink, callerPath: string -): Promise; -export async function getSiteLinksComponents( - siteLinks: SiteLinkFlatGroup[], +): Promise; +export async function getSiteLinkComponent( + siteLink: SiteLinkFlatGroup, callerPath: string -): Promise; -export async function getSiteLinksComponents( - siteLinks: SiteLinkGroup[], +): Promise; +export async function getSiteLinkComponent( + siteLink: SiteLinkGroup, callerPath: string -): Promise; -export async function getSiteLinksComponents( - siteLinks: SiteLinkAny[], +): Promise; +export async function getSiteLinkComponent( + siteLink: SiteLinkAny, callerPath: string -): Promise; -export async function getSiteLinksComponents( - siteLinks: SiteLinkAny[], +): Promise; +export async function getSiteLinkComponent( + siteLink: SiteLinkAny, callerPath: string -): Promise { +): Promise { const exts_str = ['svg']; const exts_nostr = ['svelte']; const specifiers_str = ['raw']; const specifiers_nostr = ['component']; - const siteLinksLoaded: typeof siteLinks = await Promise.all( - siteLinks.map(async (l) => { - const l1: UnwrapArray = { ...l }; - if (l.img_import) { - try { - const [prefix, img_import1] = l.img_import.includes(':') - ? l.img_import.split(':', 2) - : ['', l.img_import]; - const [img_import, specifier1] = img_import1.includes('?') - ? l.img_import.split('?', 2) - : [l.img_import, '']; - const specifier = specifier1.toLowerCase(); - const extension = getExtension(img_import); - - // What type of import data to expect? - let stringOk = false; - if (specifiers_nostr.includes(specifier) || exts_nostr.includes(extension)) { - stringOk = false; - } else if (specifiers_str.includes(specifier) || exts_str.includes(extension)) { - stringOk = true; - } + if (siteLink.img_import) { + try { + const [prefix, img_import1] = siteLink.img_import.includes(':') + ? siteLink.img_import.split(':', 2) + : ['', siteLink.img_import]; + const [img_import, specifier1] = img_import1.includes('?') + ? siteLink.img_import.split('?', 2) + : [siteLink.img_import, '']; + const specifier = specifier1.toLowerCase(); + const extension = getExtension(img_import); + + // What type of import data to expect? + let stringOk = false; + if (specifiers_nostr.includes(specifier) || exts_nostr.includes(extension)) { + stringOk = false; + } else if (specifiers_str.includes(specifier) || exts_str.includes(extension)) { + stringOk = true; + } - const im = await loadComponent(img_import); - if (!im) { - throw new Error(`Component ${img_import} not found`); - } - if (typeof im === 'string') { - // string type can be returned for svg files - either ?raw - then it's file contents, or url. - // There's no easy way to distinguish contents from url here, so decided to not handle urls here. - if (!stringOk) { - // Unfortunately, we can't currently import SVG files as components in runtime. - // Perhaps we can do `svelte.compile()` here? Or need a plugin that compiles dynamic SVG into component in build time based on dynamic import. - // throw new Error(`Expected Component, got ${typeof im} from ${pathname}`); - throw new Error(`Expected Component, got ${typeof im} from ${img_import}`); - } - l1.img_html = im; - } else { - l1.img_component = im; - } - } catch (e) { - console.log(`Error "${e}" loading module "${l.img_import}"`); - } + const im = await loadComponent(img_import); + if (!im) { + throw new Error(`Component ${img_import} not found`); } - if (l.img_icon) { - if (typeof l.img_icon === 'string') { - // Also conveniently accept `path`, `svg`, or `svgUrl` as `data` - const dataStr = l.img_icon.toLowerCase(); - if (dataStr.includes(' 0) { - (l1 as unknown as SiteLinkGroup).items = await getSiteLinksComponents(items, callerPath); + } catch (e) { + console.log(`Error "${e}" loading module "${siteLink.img_import}"`); + } + } + if (siteLink.img_icon) { + if (typeof siteLink.img_icon === 'string') { + // Also conveniently accept `path`, `svg`, or `svgUrl` as `data` + const dataStr = siteLink.img_icon.toLowerCase(); + if (dataStr.includes(' 0) { + await Promise.all(items.map((item) => getSiteLinkComponent(item, callerPath))); + } + return siteLink; } function pathsToSvg(paths: string[]): string { @@ -188,60 +182,67 @@ function pathsToSvg(paths: string[]): string { } // TODO: (now) This function badly needs UNIT TESTS for all kinds of arg combinations and siteLinks structures. -export function getSiteLinksFiltered( +export function prepSiteLinks( siteLinks: SiteLinkAny[], filter: SiteLinkFilter, treeDepth: number, nodeFilter?: boolean, - flatten?: true + flatten?: true, + prune?: boolean ): SiteLink[]; -export function getSiteLinksFiltered( +export function prepSiteLinks( siteLinks: SiteLinkAny[], filter: SiteLinkFilter, treeDepth: 1, nodeFilter?: boolean, - flatten?: false + flatten?: false, + prune?: boolean ): SiteLink[]; -export function getSiteLinksFiltered( +export function prepSiteLinks( siteLinks: SiteLink[], filter: SiteLinkFilter, treeDepth: number, nodeFilter?: boolean, - flatten?: boolean + flatten?: boolean, + prune?: boolean ): SiteLink[]; -export function getSiteLinksFiltered( +export function prepSiteLinks( siteLinks: SiteLinkFlatGroup[] | SiteLinkGroup[], filter: SiteLinkFilter, treeDepth: 2, nodeFilter?: boolean, - flatten?: boolean + flatten?: boolean, + prune?: boolean ): SiteLinkFlatGroup[]; -export function getSiteLinksFiltered( +export function prepSiteLinks( siteLinks: SiteLinkFlatGroup[], filter: SiteLinkFilter, treeDepth: number, nodeFilter?: boolean, - flatten?: boolean + flatten?: boolean, + prune?: boolean ): SiteLinkFlatGroup[]; -export function getSiteLinksFiltered( +export function prepSiteLinks( siteLinks: SiteLinkGroup[], filter: SiteLinkFilter, treeDepth?: number, nodeFilter?: boolean, - flatten?: boolean + flatten?: boolean, + prune?: boolean ): SiteLinkGroup[]; -export function getSiteLinksFiltered( +export function prepSiteLinks( siteLinks: SiteLinkAny[], filter: SiteLinkFilter, treeDepth: number = 0, // 0 - will keep all depth branches, non-0 will prune the tree to the depth nodeFilter: boolean = true, // true will filter out nodes based on filter only (igmoring if any children match the filter), false will keep nodes that have children matching the filter - flatten: boolean = false + flatten: boolean = false, + prune?: boolean ): SiteLinkAny[] { const filterField = { brand: 'displayInBrand', @@ -288,81 +289,8 @@ export function getSiteLinksFiltered( return result; } - return filterRecursively(siteLinks, 0); -} - -export async function prepSiteLinks( - siteLinks: SiteLinkAny[], - callerPath: string, - filter: SiteLinkFilter, - treeDepth: number, - nodeFilter: boolean, - flatten: true, - prune?: boolean -): Promise; - -export async function prepSiteLinks( - siteLinks: SiteLinkAny[], - callerPath: string, - filter: SiteLinkFilter, - treeDepth: 1, - nodeFilter?: boolean, - flatten?: boolean, - prune?: boolean -): Promise; - -export async function prepSiteLinks( - siteLinks: SiteLink[], - callerPath: string, - filter: SiteLinkFilter, - treeDepth: number, - nodeFilter?: boolean, - flatten?: boolean, - prune?: boolean -): Promise; - -export async function prepSiteLinks( - siteLinks: SiteLinkGroup[] | SiteLinkFlatGroup[], - callerPath: string, - filter: SiteLinkFilter, - treeDepth: 2, - nodeFilter?: boolean, - flatten?: false, - prune?: boolean -): Promise; - -export async function prepSiteLinks( - siteLinks: SiteLinkFlatGroup[], - callerPath: string, - filter: SiteLinkFilter, - treeDepth: number, - nodeFilter?: boolean, - flatten?: boolean, - prune?: boolean -): Promise; - -export async function prepSiteLinks( - siteLinks: SiteLinkGroup[], - callerPath: string, - filter: SiteLinkFilter, - treeDepth?: number, - nodeFilter?: boolean, - flatten?: boolean, - prune?: boolean -): Promise; - -export async function prepSiteLinks( - siteLinks: SiteLinkAny[], - callerPath: string, - filter: SiteLinkFilter, - treeDepth: number = 0, // 0 - will keep all depth branches, non-0 will prune the tree to the depth - nodeFilter: boolean = true, // true - remove nodes that don't match filter (ignoring any children matching the filter), false - keep nodes that have any children matching the filter - flatten: boolean = false, - prune: boolean = true // Remove elements with empty .href (only when flatten = true) -): Promise { - const filtered = getSiteLinksFiltered(siteLinks, filter, treeDepth, nodeFilter, flatten); - const pruned = flatten && prune ? pruneSiteLinks(filtered) : filtered; - const result = await getSiteLinksComponents(pruned, callerPath); + let result = filterRecursively(siteLinks, 0); + result = flatten && prune ? pruneSiteLinks(result) : result; return result; } @@ -373,3 +301,26 @@ export function pruneSiteLinks(siteLinks: SiteLinkAny[]): SiteLinkAny[] { const result = siteLinks.filter((l) => !!l?.href); return result; } + +export function loadSiteLinks(siteLinks: SiteLinkAny[], callerPath: string): Promise[]; +export function loadSiteLinks(siteLinks: SiteLinkAny[], callerPath: string): Promise[]; +export function loadSiteLinks(siteLinks: SiteLink[], callerPath: string): Promise[]; +export function loadSiteLinks( + siteLinks: SiteLinkFlatGroup[] | SiteLinkGroup[], + callerPath: string +): Promise[]; +export function loadSiteLinks( + siteLinks: SiteLinkFlatGroup[], + callerPath: string +): Promise[]; +export function loadSiteLinks( + siteLinks: SiteLinkGroup[], + callerPath: string +): Promise[]; +export function loadSiteLinks( + siteLinks: SiteLinkAny[], + callerPath: string +): Promise[] { + const result = siteLinks.map((siteLink) => getSiteLinkComponent(siteLink, callerPath)); + return result; +} diff --git a/src/routes/(demo)/+layout.svelte b/src/routes/(demo)/+layout.svelte index 6da9a3fa..591e5757 100644 --- a/src/routes/(demo)/+layout.svelte +++ b/src/routes/(demo)/+layout.svelte @@ -11,7 +11,7 @@ import { BRIGHT_ENTITY, CRESCENT_MOON_ENTITY } from '$lib/constants/entities'; import website from '$lib/config/website'; - import { prepSiteLinks } from '$lib/config/configUtils'; + import { loadSiteLinks, prepSiteLinks } from '$lib/config/configUtils'; import type { SiteLink } from '$lib/types'; const { siteLinks } = website; @@ -20,22 +20,24 @@ // let { data, children } = $props<{ data: LayoutData; children: Snippet }>(); let { children } = $props<{ children: Snippet }>(); - let footerLinks = $state([]); - - onMount(async () => { - /* DISABLED (see root +layout.svelte) - await loadIonicPWAElements(window); - */ - const mypath = import.meta.url; - footerLinks = await prepSiteLinks( + let footerLinks = $state( + prepSiteLinks( siteLinks, - mypath, 'footer', 1, /* nodeFilter */ true, /* flatten */ true, /* prune */ true - ); + ) + ); + + onMount(async () => { + /* DISABLED (see root +layout.svelte) + await loadIonicPWAElements(window); + */ + const mypath = import.meta.url; + // footerLinks = + await Promise.all(loadSiteLinks(footerLinks, mypath)); console.log('DEBUG: footerLinks=%o', footerLinks); });