@builder.io/qwik
Version:
An Open-Source sub-framework designed with a focus on server-side-rendering, lazy-loading, and styling/animation.
244 lines • 11.4 kB
JavaScript
(() => {
const DIRECT_PRIORITY = Number.MAX_SAFE_INTEGER >>> 1;
function directFetch(swState, url) {
const [basePath, filename] = parseBaseFilename(url);
const base = swState.$bases$.find((base2 => basePath === base2.$path$));
if (base) {
swState.$log$("intercepting", url.pathname);
return enqueueFileAndDependencies(swState, base, [ filename ], DIRECT_PRIORITY).then((() => function(swState, url) {
const currentRequestTask = swState.$queue$.find((task => task.$url$.pathname === url.pathname));
if (currentRequestTask) {
return currentRequestTask.$response$.then((response => response.clone()));
}
swState.$log$("CACHE HIT", url.pathname);
return swState.$match$(url);
}(swState, url)));
}
}
async function enqueueFileAndDependencies(swState, base, filenames, priority) {
const fetchMap = new Map;
filenames.forEach((filename => addDependencies(base, fetchMap, filename, priority)));
await Promise.all(Array.from(fetchMap.entries()).map((([filename, prio]) => async function(swState, url, priority) {
let task = swState.$queue$.find((task2 => task2.$url$.pathname === url.pathname));
const mode = priority >= DIRECT_PRIORITY ? "direct" : "prefetch";
if (task) {
const state = task.$isFetching$ ? "fetching" : "waiting";
if (task.$priority$ < priority) {
swState.$log$("queue update priority", state, url.pathname);
task.$priority$ = priority;
} else {
swState.$log$("already in queue", mode, state, url.pathname);
}
} else {
if (!await swState.$match$(url)) {
swState.$log$("enqueue", mode, url.pathname);
task = {
$priority$: priority,
$url$: url,
$resolveResponse$: null,
$response$: null,
$isFetching$: !1
};
task.$response$ = new Promise((resolve => task.$resolveResponse$ = resolve));
swState.$queue$.push(task);
}
}
return task;
}(swState, new URL(base.$path$ + filename, swState.$url$.origin), prio))));
taskTick(swState);
}
function taskTick(swState) {
swState.$queue$.sort(byFetchOrder);
let outstandingRequests = 0;
for (const task of swState.$queue$) {
if (task.$isFetching$) {
outstandingRequests++;
} else if (swState.$getCache$() && (outstandingRequests < swState.$maxPrefetchRequests$ || task.$priority$ >= DIRECT_PRIORITY)) {
task.$isFetching$ = !0;
outstandingRequests++;
const action = task.$priority$ >= DIRECT_PRIORITY ? "FETCH (CACHE MISS)" : "FETCH";
swState.$log$(action, task.$url$.pathname);
swState.$fetch$(task.$url$).then((async response => {
task.$resolveResponse$(response);
if (200 === response.status) {
swState.$log$("CACHED", task.$url$.pathname);
await swState.$put$(task.$url$, response.clone());
}
})).finally((() => {
swState.$log$("FETCH DONE", task.$url$.pathname);
swState.$queue$.splice(swState.$queue$.indexOf(task), 1);
taskTick(swState);
}));
}
}
}
function byFetchOrder(a, b) {
return b.$priority$ - a.$priority$;
}
function addDependencies(base, fetchMap, filename, priority, addIndirect = !0) {
if (!fetchMap.has(filename)) {
fetchMap.set(filename, priority);
if (!base.$processed$) {
base.$processed$ = new Map;
let current, isDirect;
for (let i = 0; i < base.$graph$.length; i++) {
const item = base.$graph$[i];
if ("string" == typeof item) {
current = {
$direct$: [],
$indirect$: []
};
isDirect = !0;
base.$processed$.set(item, current);
} else if (-1 === item) {
isDirect = !1;
} else {
const depName = base.$graph$[item];
isDirect ? current.$direct$.push(depName) : current.$indirect$.push(depName);
}
}
}
const deps = base.$processed$.get(filename);
if (!deps) {
return fetchMap;
}
for (const dependentFilename of deps.$direct$) {
addDependencies(base, fetchMap, dependentFilename, priority);
}
if (addIndirect) {
priority--;
for (const dependentFilename of deps.$indirect$) {
addDependencies(base, fetchMap, dependentFilename, priority, !1);
}
}
}
return fetchMap;
}
function parseBaseFilename(url) {
const pathname = new URL(url).pathname;
const slashIndex = pathname.lastIndexOf("/");
return [ pathname.substring(0, slashIndex + 1), pathname.substring(slashIndex + 1) ];
}
const log = (...args) => {
console.log("⚙️ Prefetch SW:", ...args);
};
const processMessage = async (state, msg) => {
const type = msg[0];
state.$log$("received message:", type, msg[1], msg.slice(2));
"graph" === type ? await processBundleGraph(state, msg[1], msg.slice(2), !0) : "graph-url" === type ? await async function(swState, base, graphPath) {
await processBundleGraph(swState, base, [], !1);
const response = await directFetch(swState, new URL(base + graphPath, swState.$url$.origin));
if (response && 200 === response.status) {
const graph = await response.json();
graph.push(graphPath);
await processBundleGraph(swState, base, graph, !0);
}
}(state, msg[1], msg[2]) : "prefetch" === type ? await processPrefetch(state, msg[1], msg.slice(2)) : "prefetch-all" === type ? await function(swState, basePath) {
const base = swState.$bases$.find((base2 => basePath === base2.$path$));
base ? processPrefetch(swState, basePath, base.$graph$.filter((item => "string" == typeof item))) : console.error(`Base path not found: ${basePath}, ignoring prefetch.`);
}(state, msg[1]) : "ping" === type ? log("ping") : "verbose" === type ? (state.$log$ = log)("mode: verbose") : console.error("UNKNOWN MESSAGE:", msg);
};
async function processBundleGraph(swState, base, graph, cleanup) {
const existingBaseIndex = swState.$bases$.findIndex((b => b.$path$ === base));
-1 !== existingBaseIndex && swState.$bases$.splice(existingBaseIndex, 1);
swState.$log$("adding base:", base);
swState.$bases$.push({
$path$: base,
$graph$: graph,
$processed$: void 0
});
if (cleanup) {
const bundles = new Set(graph.filter((item => "string" == typeof item)));
const cache = await swState.$getCache$();
if (cache) {
for (const request of await cache.keys()) {
const [cacheBase, filename] = parseBaseFilename(new URL(request.url));
const promises = [];
if (cacheBase === base && !bundles.has(filename)) {
swState.$log$("deleting", request.url);
promises.push(cache.delete(request));
}
await Promise.all(promises);
}
}
}
}
function processPrefetch(swState, basePath, bundles) {
let base = swState.$bases$.find((base2 => base2.$graph$.includes(bundles[0].replace("./", ""))));
base || (base = swState.$bases$.find((base2 => basePath === base2.$path$)));
base ? enqueueFileAndDependencies(swState, base, bundles, 0) : console.error(`Base path not found: ${basePath}, ignoring prefetch.`);
}
function drainMsgQueue(swState) {
if (!swState.$msgQueuePromise$ && swState.$msgQueue$.length) {
const top = swState.$msgQueue$.shift();
swState.$msgQueuePromise$ = processMessage(swState, top).then((() => {
swState.$msgQueuePromise$ = null;
drainMsgQueue(swState);
}));
}
}
class SWStateImpl {
constructor($fetch$, $url$, $maxPrefetchRequests$ = 4, $cache$ = null, $msgQueuePromise$ = null, $queue$ = [], $bases$ = [], $msgQueue$ = []) {
this.$fetch$ = $fetch$;
this.$url$ = $url$;
this.$maxPrefetchRequests$ = $maxPrefetchRequests$;
this.$cache$ = $cache$;
this.$msgQueuePromise$ = $msgQueuePromise$;
this.$queue$ = $queue$;
this.$bases$ = $bases$;
this.$msgQueue$ = $msgQueue$;
}
$getCache$() {
return this.$cache$;
}
async $put$(request, response) {
const cache = await this.$getCache$();
return null == cache ? void 0 : cache.put(request, response);
}
async $match$(request) {
const cache = await this.$getCache$();
return null == cache ? void 0 : cache.match(request);
}
$log$() {}
}
(swScope => {
const swState = ((fetch, url) => new SWStateImpl(fetch, url))(swScope.fetch.bind(swScope), new URL(swScope.location.href));
swState.$getCache$ = () => {
if (swState.$cache$) {
return swState.$cache$;
}
clearTimeout(undefined);
setTimeout((() => {
swState.$cache$ = null;
}), 5e3);
return swScope.caches.open("QwikBundles");
};
swScope.addEventListener("fetch", (ev => {
const request = ev.request;
if ("GET" === request.method) {
const response = directFetch(swState, new URL(request.url));
response && ev.respondWith(response);
}
}));
swScope.addEventListener("message", (ev => {
swState.$msgQueue$.push(ev.data);
drainMsgQueue(swState);
}));
swScope.addEventListener("install", (() => {
swScope.skipWaiting();
}));
swScope.addEventListener("activate", (event => {
swState.$getCache$ = () => {
if (swState.$cache$) {
return swState.$cache$;
}
clearTimeout(undefined);
setTimeout((() => {
swState.$cache$ = null;
}), 5e3);
return swScope.caches.open("QwikBundles");
};
event.waitUntil(swScope.clients.claim());
}));
})(globalThis);
})();