From 49250a1b1cd2a955acae4085cb3e79bcc54f6c0c Mon Sep 17 00:00:00 2001
From: Adam Bradley
Date: Sat, 3 Sep 2022 21:44:42 -0500
Subject: [PATCH] feat: reduce prefetch request waterfall (#1204)
---
.../generate-service-worker.ts | 103 ++++++++++++++++--
packages/qwik-city/buildtime/vite/plugin.ts | 2 +-
.../request-handler/page-handler.ts | 8 --
.../runtime/src/library/client-navigate.ts | 4 +
.../src/library/service-worker/index.ts | 8 +-
.../src/library/service-worker/prefetch.ts | 102 +++++++++++++----
.../src/library/service-worker/setup.ts | 36 +++---
.../src/library/service-worker/types.ts | 9 +-
.../src/library/service-worker/utils.ts | 25 ++---
.../src/library/service-worker/utils.unit.ts | 34 +++---
.../runtime/src/library/use-endpoint.ts | 52 +++++----
11 files changed, 267 insertions(+), 116 deletions(-)
diff --git a/packages/qwik-city/buildtime/runtime-generation/generate-service-worker.ts b/packages/qwik-city/buildtime/runtime-generation/generate-service-worker.ts
index 47f1ebe7116..f33f4bd95ca 100644
--- a/packages/qwik-city/buildtime/runtime-generation/generate-service-worker.ts
+++ b/packages/qwik-city/buildtime/runtime-generation/generate-service-worker.ts
@@ -1,7 +1,8 @@
import type { BuildContext } from '../types';
import swRegister from '@qwik-city-sw-register-build';
import type { QwikManifest } from '@builder.io/qwik/optimizer';
-import type { AppBundles } from '../../runtime/src/library/service-worker/types';
+import type { AppBundle } from '../../runtime/src/library/service-worker/types';
+import { removeExtension } from '../../utils/fs';
export function generateServiceWorkerRegister(ctx: BuildContext) {
let swReg: string;
@@ -25,7 +26,11 @@ export function generateServiceWorkerRegister(ctx: BuildContext) {
return `export default ${JSON.stringify(swReg)};`;
}
-export function prependManifestToServiceWorker(manifest: QwikManifest, swCode: string) {
+export function prependManifestToServiceWorker(
+ ctx: BuildContext,
+ manifest: QwikManifest,
+ swCode: string
+) {
const key = `/* Qwik Service Worker */`;
if (swCode.includes(key)) {
// both SSR and SSG could have ran this code,
@@ -33,34 +38,110 @@ export function prependManifestToServiceWorker(manifest: QwikManifest, swCode: s
return null;
}
- const appBundlesCode = generateAppBundles(manifest);
+ const appBundles: AppBundle[] = [];
+ const appBundlesCode = generateAppBundles(appBundles, manifest);
+ const libraryBundlesCode = generateLibraryBundles(appBundles, manifest);
+ const linkBundlesCode = generateLinkBundles(ctx, appBundles, manifest);
- return [key, appBundlesCode, swCode].join('\n');
+ return [key, appBundlesCode, libraryBundlesCode, linkBundlesCode, swCode].join('\n');
}
-function generateAppBundles(manifest: QwikManifest) {
- const appBundles: AppBundles = {};
-
+function generateAppBundles(appBundles: AppBundle[], manifest: QwikManifest) {
for (const appBundleName in manifest.bundles) {
+ appBundles.push([appBundleName, []]);
+ }
+
+ for (const appBundle of appBundles) {
+ const appBundleName = appBundle[0];
+ const importedBundleIds = appBundle[1];
+ const symbolHashesInBundle: string[] = [];
+
const manifestBundle = manifest.bundles[appBundleName];
const importedBundleNames = Array.isArray(manifestBundle.imports) ? manifestBundle.imports : [];
- const symbolHashesInBundle = new Set();
+ for (const importedBundleName of importedBundleNames) {
+ importedBundleIds.push(getAppBundleId(appBundles, importedBundleName));
+ }
if (manifestBundle.symbols) {
for (const manifestBundleSymbolName of manifestBundle.symbols) {
const symbol = manifest.symbols[manifestBundleSymbolName];
- if (symbol?.hash) {
- symbolHashesInBundle.add(symbol.hash);
+ if (symbol?.hash && !symbolHashesInBundle.includes(symbol.hash)) {
+ symbolHashesInBundle.push(symbol.hash);
}
}
}
- appBundles[appBundleName] = [importedBundleNames, Array.from(symbolHashesInBundle)];
+ if (symbolHashesInBundle.length > 0) {
+ appBundle[2] = symbolHashesInBundle;
+ }
}
return `const appBundles=${JSON.stringify(appBundles)};`;
}
+function generateLibraryBundles(appBundles: AppBundle[], manifest: QwikManifest) {
+ const libraryBundleIds: number[] = [];
+
+ for (const [bundleName, bundle] of Object.entries(manifest.bundles)) {
+ if (bundle.origins && bundle.origins.includes('@qwik-city-plan')) {
+ libraryBundleIds.push(getAppBundleId(appBundles, bundleName));
+ break;
+ }
+ }
+
+ return `const libraryBundleIds=${JSON.stringify(libraryBundleIds)};`;
+}
+
+function generateLinkBundles(ctx: BuildContext, appBundles: AppBundle[], manifest: QwikManifest) {
+ const linkBundles: string[] = [];
+
+ for (const r of ctx.routes) {
+ const linkBundleNames: string[] = [];
+
+ const addFileBundles = (filePath: string) => {
+ for (const [bundleName, bundle] of Object.entries(manifest.bundles)) {
+ if (bundle.origins) {
+ for (const bundleOrigin of bundle.origins) {
+ const srcPath = removeExtension(filePath);
+ const bundleOrginPath = removeExtension(bundleOrigin);
+
+ if (srcPath.endsWith(bundleOrginPath)) {
+ if (!linkBundleNames.includes(bundleName)) {
+ linkBundleNames.push(bundleName);
+ }
+
+ if (bundle.dynamicImports) {
+ for (const dynamicImport of bundle.dynamicImports) {
+ if (!linkBundleNames.includes(dynamicImport)) {
+ linkBundleNames.push(dynamicImport);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+ for (const layout of r.layouts) {
+ addFileBundles(layout.filePath);
+ }
+ addFileBundles(r.filePath);
+
+ linkBundles.push(
+ `[${r.pattern.toString()},${JSON.stringify(
+ linkBundleNames.map((bundleName) => getAppBundleId(appBundles, bundleName))
+ )}]`
+ );
+ }
+
+ return `const linkBundles=[${linkBundles.join(',')}];`;
+}
+
+function getAppBundleId(appBundles: AppBundle[], bundleName: string) {
+ return appBundles.findIndex((b) => b[0] === bundleName);
+}
+
const SW_UNREGISTER = `
navigator.serviceWorker.getRegistrations().then((regs) => {
for (const reg of regs) {
diff --git a/packages/qwik-city/buildtime/vite/plugin.ts b/packages/qwik-city/buildtime/vite/plugin.ts
index bda1f9156b1..548a62b3b78 100644
--- a/packages/qwik-city/buildtime/vite/plugin.ts
+++ b/packages/qwik-city/buildtime/vite/plugin.ts
@@ -201,7 +201,7 @@ export function qwikCity(userOpts?: QwikCityVitePluginOptions) {
const swClientDistPath = join(clientOutDir, swEntry.chunkFileName);
const swCode = await readFile(swClientDistPath, 'utf-8');
- const swCodeUpdate = prependManifestToServiceWorker(manifest, swCode);
+ const swCodeUpdate = prependManifestToServiceWorker(ctx, manifest, swCode);
if (swCodeUpdate) {
await writeFile(swClientDistPath, swCodeUpdate);
}
diff --git a/packages/qwik-city/middleware/request-handler/page-handler.ts b/packages/qwik-city/middleware/request-handler/page-handler.ts
index 4e2d6b00577..dd3e7cb6d76 100644
--- a/packages/qwik-city/middleware/request-handler/page-handler.ts
+++ b/packages/qwik-city/middleware/request-handler/page-handler.ts
@@ -110,14 +110,6 @@ function getPrefetchBundleNames(result: RenderResult, routeBundleNames: string[]
addBundle(manifest.mapping[renderedSymbolName]);
}
}
-
- // and the router regex map
- for (const [bundleName, bundle] of Object.entries(manifest.bundles)) {
- if (bundle.origins && bundle.origins.includes('@qwik-city-plan')) {
- addBundle(bundleName);
- break;
- }
- }
}
if (routeBundleNames) {
diff --git a/packages/qwik-city/runtime/src/library/client-navigate.ts b/packages/qwik-city/runtime/src/library/client-navigate.ts
index b35ef12e8cb..2e897e1d0a8 100644
--- a/packages/qwik-city/runtime/src/library/client-navigate.ts
+++ b/packages/qwik-city/runtime/src/library/client-navigate.ts
@@ -1,3 +1,4 @@
+import type { QPrefetchData } from './service-worker/types';
import type { RouteNavigate, SimpleURL } from './types';
import { isSameOriginDifferentPathname, isSamePath, toPath, toUrl } from './utils';
@@ -88,6 +89,9 @@ const scrollToHashId = (doc: Document, hash: string) => {
return elm;
};
+export const dispatchPrefetchEvent = (prefetchData: QPrefetchData) =>
+ dispatchEvent(new CustomEvent('qprefetch', { detail: prefetchData }));
+
export const CLIENT_HISTORY_INITIALIZED = /* @__PURE__ */ Symbol();
export interface ClientHistoryWindow extends Window {
diff --git a/packages/qwik-city/runtime/src/library/service-worker/index.ts b/packages/qwik-city/runtime/src/library/service-worker/index.ts
index 9222d2b4762..23f186c509b 100644
--- a/packages/qwik-city/runtime/src/library/service-worker/index.ts
+++ b/packages/qwik-city/runtime/src/library/service-worker/index.ts
@@ -1,4 +1,4 @@
-import type { AppBundles } from './types';
+import type { AppBundle, LinkBundle } from './types';
import { setupServiceWorkerScope } from './setup';
/**
@@ -6,8 +6,10 @@ import { setupServiceWorkerScope } from './setup';
*/
export const setupServiceWorker = () => {
if (typeof self !== 'undefined' && typeof appBundles !== 'undefined') {
- setupServiceWorkerScope(self as any, appBundles);
+ setupServiceWorkerScope(self as any, appBundles, libraryBundleIds, linkBundles);
}
};
-declare const appBundles: AppBundles;
+declare const appBundles: AppBundle[];
+declare const libraryBundleIds: number[];
+declare const linkBundles: LinkBundle[];
diff --git a/packages/qwik-city/runtime/src/library/service-worker/prefetch.ts b/packages/qwik-city/runtime/src/library/service-worker/prefetch.ts
index d7f8730cd10..6d58aebb7d2 100644
--- a/packages/qwik-city/runtime/src/library/service-worker/prefetch.ts
+++ b/packages/qwik-city/runtime/src/library/service-worker/prefetch.ts
@@ -1,39 +1,97 @@
-import type { Fetch, AppBundles } from './types';
-import { cachedFetch } from './cached-fetch';
+import type { AppBundle, Fetch, LinkBundle } from './types';
import { awaitingRequests, existingPrefetches } from './constants';
+import { cachedFetch } from './cached-fetch';
+import { getAppBundleByName, getAppBundlesNamesFromIds } from './utils';
export const prefetchBundleNames = (
- appBundles: AppBundles,
+ appBundles: AppBundle[],
qBuildCache: Cache,
fetch: Fetch,
baseUrl: URL,
- activeDomQKeys: string[] | undefined,
- prefetchAppBundleNames: string[]
+ prefetchAppBundleNames: (string | null)[] | undefined | null
) => {
- const prefetchAppBundle = (prefetchAppBundleName: string) => {
- const appBundle = appBundles[prefetchAppBundleName];
+ const prefetchAppBundle = (prefetchAppBundleName: string | null) => {
+ try {
+ const appBundle = getAppBundleByName(appBundles, prefetchAppBundleName);
- if (appBundle && !existingPrefetches.has(prefetchAppBundleName)) {
- try {
- existingPrefetches.add(prefetchAppBundleName);
+ if (appBundle && !existingPrefetches.has(prefetchAppBundleName!)) {
+ existingPrefetches.add(prefetchAppBundleName!);
- const [importedBundleNames, symbolHashesInBundle] = appBundle;
+ const importedBundleNames = getAppBundlesNamesFromIds(appBundles, appBundle[1]);
+ const url = new URL(prefetchAppBundleName!, baseUrl);
+ const request = new Request(url);
- const symbolActiveInDom =
- Array.isArray(activeDomQKeys) &&
- activeDomQKeys.some((qKey) => symbolHashesInBundle.includes(qKey));
-
- if (!symbolActiveInDom) {
- const url = new URL(prefetchAppBundleName, baseUrl).href;
- cachedFetch(qBuildCache, fetch, awaitingRequests, new Request(url));
- }
+ cachedFetch(qBuildCache, fetch, awaitingRequests, request);
importedBundleNames.forEach(prefetchAppBundle);
- } catch (e) {
- console.error(e);
}
+ } catch (e) {
+ console.error(e);
}
};
- prefetchAppBundleNames.forEach(prefetchAppBundle);
+ if (Array.isArray(prefetchAppBundleNames)) {
+ prefetchAppBundleNames.forEach(prefetchAppBundle);
+ }
+};
+
+export const prefetchLinkBundles = (
+ appBundles: AppBundle[],
+ libraryBundleIds: number[],
+ linkBundles: LinkBundle[],
+ qBuildCache: Cache,
+ fetch: Fetch,
+ baseUrl: URL,
+ linkPathnames: string[]
+) => {
+ try {
+ prefetchBundleNames(
+ appBundles,
+ qBuildCache,
+ fetch,
+ baseUrl,
+ getAppBundlesNamesFromIds(appBundles, libraryBundleIds)
+ );
+ } catch (e) {
+ console.error(e);
+ }
+
+ for (const linkPathname of linkPathnames) {
+ try {
+ for (const linkBundle of linkBundles) {
+ const [route, linkBundleIds] = linkBundle;
+ console;
+ if (route.test(linkPathname)) {
+ prefetchBundleNames(
+ appBundles,
+ qBuildCache,
+ fetch,
+ baseUrl,
+ getAppBundlesNamesFromIds(appBundles, linkBundleIds)
+ );
+ break;
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ }
+};
+
+export const prefetchWaterfall = (
+ appBundles: AppBundle[],
+ qBuildCache: Cache,
+ fetch: Fetch,
+ requestedBuildUrl: URL
+) => {
+ try {
+ const segments = requestedBuildUrl.href.split('/');
+ const requestedBundleName = segments[segments.length - 1];
+ segments[segments.length - 1] = '';
+ const baseUrl = new URL(segments.join('/'));
+
+ prefetchBundleNames(appBundles, qBuildCache, fetch, baseUrl, [requestedBundleName]);
+ } catch (e) {
+ console.error(e);
+ }
};
diff --git a/packages/qwik-city/runtime/src/library/service-worker/setup.ts b/packages/qwik-city/runtime/src/library/service-worker/setup.ts
index 8205f5d0ddf..1991aa4144c 100644
--- a/packages/qwik-city/runtime/src/library/service-worker/setup.ts
+++ b/packages/qwik-city/runtime/src/library/service-worker/setup.ts
@@ -1,12 +1,14 @@
-import type { AppBundles, ServiceWorkerMessageEvent } from './types';
+import type { AppBundle, LinkBundle, ServiceWorkerMessageEvent } from './types';
import { awaitingRequests, qBuildCacheName } from './constants';
import { cachedFetch } from './cached-fetch';
import { getCacheToDelete, isAppBundleRequest } from './utils';
-import { prefetchBundleNames } from './prefetch';
+import { prefetchBundleNames, prefetchLinkBundles, prefetchWaterfall } from './prefetch';
export const setupServiceWorkerScope = (
swScope: ServiceWorkerGlobalScope,
- appBundles: AppBundles
+ appBundles: AppBundle[],
+ libraryBundleIds: number[],
+ linkBundles: LinkBundle[]
) => {
swScope.addEventListener('fetch', (ev) => {
const request = ev.request;
@@ -17,9 +19,10 @@ export const setupServiceWorkerScope = (
if (isAppBundleRequest(appBundles, url.pathname)) {
const nativeFetch = swScope.fetch.bind(swScope);
ev.respondWith(
- swScope.caches
- .open(qBuildCacheName)
- .then((qrlCache) => cachedFetch(qrlCache, nativeFetch, awaitingRequests, request))
+ swScope.caches.open(qBuildCacheName).then((qBuildCache) => {
+ prefetchWaterfall(appBundles, qBuildCache, nativeFetch, url);
+ return cachedFetch(qBuildCache, nativeFetch, awaitingRequests, request);
+ })
);
}
}
@@ -27,20 +30,25 @@ export const setupServiceWorkerScope = (
swScope.addEventListener('message', async ({ data }: ServiceWorkerMessageEvent) => {
if (data.type === 'qprefetch' && typeof data.base === 'string') {
- if (Array.isArray(data.bundles)) {
- const nativeFetch = swScope.fetch.bind(swScope);
- const qBuildCache = await swScope.caches.open(qBuildCacheName);
- const baseUrl = new URL(data.base, swScope.origin);
+ const nativeFetch = swScope.fetch.bind(swScope);
+ const qBuildCache = await swScope.caches.open(qBuildCacheName);
+ const baseUrl = new URL(data.base, swScope.origin);
- prefetchBundleNames(
+ if (Array.isArray(data.links)) {
+ prefetchLinkBundles(
appBundles,
+ libraryBundleIds,
+ linkBundles,
qBuildCache,
- nativeFetch,
+ fetch,
baseUrl,
- data.qKeys,
- data.bundles
+ data.links
);
}
+
+ if (Array.isArray(data.bundles)) {
+ prefetchBundleNames(appBundles, qBuildCache, nativeFetch, baseUrl, data.bundles);
+ }
}
});
diff --git a/packages/qwik-city/runtime/src/library/service-worker/types.ts b/packages/qwik-city/runtime/src/library/service-worker/types.ts
index 493ec7632f9..27dce131f38 100644
--- a/packages/qwik-city/runtime/src/library/service-worker/types.ts
+++ b/packages/qwik-city/runtime/src/library/service-worker/types.ts
@@ -1,4 +1,5 @@
export interface QPrefetchData {
+ links?: string[];
bundles?: string[];
qKeys?: string[];
}
@@ -14,11 +15,11 @@ export interface ServiceWorkerMessageEvent {
data: ServiceWorkerMessage;
}
-export type AppBundle = [importedBundleNames: string[], symbolHashesInBundle: string[]];
+export type AppBundle =
+ | [bundleName: string, importedBundleIds: number[]]
+ | [bundleName: string, importedBundleIds: number[], symbolHashesInBundle: string[]];
-export interface AppBundles {
- [bundleName: string]: AppBundle;
-}
+export type LinkBundle = [routePattern: RegExp, bundleIds: number[]];
export type Fetch = (r: Request) => Promise;
diff --git a/packages/qwik-city/runtime/src/library/service-worker/utils.ts b/packages/qwik-city/runtime/src/library/service-worker/utils.ts
index 4a1c536c589..997491c37f7 100644
--- a/packages/qwik-city/runtime/src/library/service-worker/utils.ts
+++ b/packages/qwik-city/runtime/src/library/service-worker/utils.ts
@@ -1,10 +1,7 @@
-import type { AppBundles } from './types';
+import type { AppBundle } from './types';
-export const getCacheToDelete = (appBundles: AppBundles, cachedUrls: string[]) => {
- const appBundleNames = Object.keys(appBundles);
- return cachedUrls.filter(
- (url) => !appBundleNames.some((appBundleName) => url.endsWith(appBundleName))
- );
+export const getCacheToDelete = (appBundles: AppBundle[], cachedUrls: string[]) => {
+ return cachedUrls.filter((url) => !appBundles.some((appBundle) => url.endsWith(appBundle[0])));
};
export const useCache = (request: Request, response: Response | undefined) =>
@@ -15,11 +12,11 @@ const hasNoCacheHeader = (r: { headers: Headers }) => {
return cacheControl.includes('no-cache') || cacheControl.includes('max-age=0');
};
-export const isAppBundleRequest = (appBundles: AppBundles, requestPathname: string) => {
- for (const appBundleName in appBundles) {
- if (requestPathname.endsWith(appBundleName)) {
- return true;
- }
- }
- return false;
-};
+export const isAppBundleRequest = (appBundles: AppBundle[], requestPathname: string) =>
+ appBundles.some((b) => requestPathname.endsWith('/' + b[0]));
+
+export const getAppBundleByName = (appBundles: AppBundle[], appBundleName: string | null) =>
+ appBundles.find((b) => b[0] === appBundleName);
+
+export const getAppBundlesNamesFromIds = (appBundles: AppBundle[], bundleIds: number[]) =>
+ bundleIds.map((bundleId) => (appBundles[bundleId] ? appBundles[bundleId][0] : null));
diff --git a/packages/qwik-city/runtime/src/library/service-worker/utils.unit.ts b/packages/qwik-city/runtime/src/library/service-worker/utils.unit.ts
index 94b8b256c54..889ad3a1017 100644
--- a/packages/qwik-city/runtime/src/library/service-worker/utils.unit.ts
+++ b/packages/qwik-city/runtime/src/library/service-worker/utils.unit.ts
@@ -1,14 +1,14 @@
import { test } from 'uvu';
import { equal } from 'uvu/assert';
import { Request as NodeRequest, Response as NodeResponse } from 'node-fetch';
-import type { AppBundles } from './types';
+import type { AppBundle } from './types';
import { getCacheToDelete, isAppBundleRequest, useCache } from './utils';
test('getCacheToDelete, delete bundles no longer possible', () => {
- const appBundles: AppBundles = {
- 'q-abc.js': [[], []],
- 'q-def.js': [[], []],
- };
+ const appBundles: AppBundle[] = [
+ ['q-abc.js', [], []],
+ ['q-def.js', [], []],
+ ];
const cachedUrls = [
'https://qwik.builder.io/build/q-abc.js',
'https://qwik.builder.io/build/q-xyz.js',
@@ -18,30 +18,30 @@ test('getCacheToDelete, delete bundles no longer possible', () => {
});
test('getCacheToDelete, none to delete', () => {
- const appBundles: AppBundles = {
- 'q-abc.js': [[], []],
- 'q-def.js': [[], []],
- };
+ const appBundles: AppBundle[] = [
+ ['q-abc.js', [], []],
+ ['q-def.js', [], []],
+ ];
const cachedUrls = ['https://qwik.builder.io/build/q-abc.js'];
const c = getCacheToDelete(appBundles, cachedUrls);
equal(c, []);
});
test('isAppBundleRequest, in buildBundles', () => {
- const appBundles: AppBundles = {
- 'q-abc.js': [[], []],
- 'q-def.js': [[], []],
- };
+ const appBundles: AppBundle[] = [
+ ['q-abc.js', [], []],
+ ['q-def.js', [], []],
+ ];
const pathname = '/build/q-abc.js';
const c = isAppBundleRequest(appBundles, pathname);
equal(c, true);
});
test('isAppBundleRequest, not in buildBundles', () => {
- const appBundles: AppBundles = {
- 'q-abc.js': [[], []],
- 'q-def.js': [[], []],
- };
+ const appBundles: AppBundle[] = [
+ ['q-abc.js', [], []],
+ ['q-def.js', [], []],
+ ];
const pathname = '/build/q-xyz.js';
const c = isAppBundleRequest(appBundles, pathname);
equal(c, false);
diff --git a/packages/qwik-city/runtime/src/library/use-endpoint.ts b/packages/qwik-city/runtime/src/library/use-endpoint.ts
index d492c3a3b76..6cf6fb318c0 100644
--- a/packages/qwik-city/runtime/src/library/use-endpoint.ts
+++ b/packages/qwik-city/runtime/src/library/use-endpoint.ts
@@ -3,8 +3,8 @@ import { useLocation, useQwikCityEnv } from './use-functions';
import { isServer } from '@builder.io/qwik/build';
import type { ClientPageData, GetEndpointData } from './types';
import { getClientEndpointPath } from './utils';
-import type { QPrefetchData } from './service-worker/types';
import { cacheModules } from '@qwik-city-plan';
+import { dispatchPrefetchEvent } from './client-navigate';
/**
* @alpha
@@ -30,13 +30,19 @@ export const useEndpoint = () => {
};
export const loadClientData = async (href: string) => {
- const endpointUrl = getClientEndpointPath(new URL(href).pathname);
+ const pagePathname = new URL(href).pathname;
+ const endpointUrl = getClientEndpointPath(pagePathname);
const now = Date.now();
const expiration = cacheModules ? 600000 : 15000;
const cachedClientPageIndex = cachedClientPages.findIndex((c) => c.u === endpointUrl);
+
let cachedClientPageData = cachedClientPages[cachedClientPageIndex];
+ dispatchPrefetchEvent({
+ links: [pagePathname],
+ });
+
if (!cachedClientPageData || cachedClientPageData.t + expiration < now) {
cachedClientPageData = {
u: endpointUrl,
@@ -48,11 +54,11 @@ export const loadClientData = async (href: string) => {
if (clientResponse.ok && contentType.includes('json')) {
clientResponse.json().then(
(clientData: ClientPageData) => {
- const prefetchData: QPrefetchData = {
+ dispatchPrefetchEvent({
bundles: clientData.prefetch,
- qKeys: getDocumentQKeys(document),
- };
- dispatchEvent(new CustomEvent('qprefetch', { detail: prefetchData }));
+ links: [pagePathname],
+ // qKeys: getDocumentQKeys(document),
+ });
resolve(clientData);
},
() => resolve(null)
@@ -74,28 +80,30 @@ export const loadClientData = async (href: string) => {
cachedClientPages.push(cachedClientPageData);
}
+ cachedClientPageData.c.catch((e) => console.error(e));
+
return cachedClientPageData.c;
};
-export const getDocumentQKeys = (doc: Document) => {
- let comment: Comment | null | undefined;
- let data: string;
- let attrIndex: number;
+// export const getDocumentQKeys = (doc: Document) => {
+// let comment: Comment | null | undefined;
+// let data: string;
+// let attrIndex: number;
- const walker = doc.createTreeWalker(doc, /* SHOW_COMMENT */ 128);
- const qKeys = new Set();
+// const walker = doc.createTreeWalker(doc, /* SHOW_COMMENT */ 128);
+// const qKeys = new Set();
- while ((comment = walker.nextNode() as any)) {
- data = comment.data;
- attrIndex = data.indexOf('q:key=');
- if (attrIndex > -1) {
- data = data.slice(attrIndex + 6);
- qKeys.add(data.slice(0, data.indexOf(':')));
- }
- }
+// while ((comment = walker.nextNode() as any)) {
+// data = comment.data;
+// attrIndex = data.indexOf('q:key=');
+// if (attrIndex > -1) {
+// data = data.slice(attrIndex + 6);
+// qKeys.add(data.slice(0, data.indexOf(':')));
+// }
+// }
- return Array.from(qKeys);
-};
+// return Array.from(qKeys);
+// };
const cachedClientPages: CachedClientPageData[] = [];