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[] = [];