ilink-world/docs/.docz/.cache/commonjs/loader.js

437 lines
12 KiB
JavaScript
Raw Normal View History

2020-03-12 15:14:51 +00:00
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.default = exports.publicLoader = exports.setLoader = exports.ProdLoader = exports.BaseLoader = void 0;
var _prefetch = _interopRequireDefault(require("./prefetch"));
var _emitter = _interopRequireDefault(require("./emitter"));
var _findPath = require("./find-path");
const preferDefault = m => m && m.default || m;
const stripSurroundingSlashes = s => {
s = s[0] === `/` ? s.slice(1) : s;
s = s.endsWith(`/`) ? s.slice(0, -1) : s;
return s;
};
const createPageDataUrl = path => {
const fixedPath = path === `/` ? `index` : stripSurroundingSlashes(path);
return `${__PATH_PREFIX__}/page-data/${fixedPath}/page-data.json`;
};
const doFetch = (url, method = `GET`) => new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open(method, url, true);
req.onreadystatechange = () => {
if (req.readyState == 4) {
resolve(req);
}
};
req.send(null);
});
const loadPageDataJson = loadObj => {
const {
pagePath,
retries = 0
} = loadObj;
const url = createPageDataUrl(pagePath);
return doFetch(url).then(req => {
const {
status,
responseText
} = req; // Handle 200
if (status === 200) {
try {
const jsonPayload = JSON.parse(responseText);
if (jsonPayload.path === undefined) {
throw new Error(`not a valid pageData response`);
}
return Object.assign(loadObj, {
status: `success`,
payload: jsonPayload
});
} catch (err) {// continue regardless of error
}
} // Handle 404
if (status === 404 || status === 200) {
// If the request was for a 404 page and it doesn't exist, we're done
if (pagePath === `/404.html`) {
return Object.assign(loadObj, {
status: `failure`
});
} // Need some code here to cache the 404 request. In case
// multiple loadPageDataJsons result in 404s
return loadPageDataJson(Object.assign(loadObj, {
pagePath: `/404.html`,
notFound: true
}));
} // handle 500 response (Unrecoverable)
if (status === 500) {
return Object.assign(loadObj, {
status: `error`
});
} // Handle everything else, including status === 0, and 503s. Should retry
if (retries < 3) {
return loadPageDataJson(Object.assign(loadObj, {
retries: retries + 1
}));
} // Retried 3 times already, result is a failure.
return Object.assign(loadObj, {
status: `error`
});
});
};
const doesConnectionSupportPrefetch = () => {
if (`connection` in navigator && typeof navigator.connection !== `undefined`) {
if ((navigator.connection.effectiveType || ``).includes(`2g`)) {
return false;
}
if (navigator.connection.saveData) {
return false;
}
}
return true;
};
const toPageResources = (pageData, component = null) => {
const page = {
componentChunkName: pageData.componentChunkName,
path: pageData.path,
webpackCompilationHash: pageData.webpackCompilationHash,
matchPath: pageData.matchPath
};
return {
component,
json: pageData.result,
page
};
};
class BaseLoader {
constructor(loadComponent, matchPaths) {
// Map of pagePath -> Page. Where Page is an object with: {
// status: `success` || `error`,
// payload: PageResources, // undefined if `error`
// }
// PageResources is {
// component,
// json: pageData.result,
// page: {
// componentChunkName,
// path,
// webpackCompilationHash,
// }
// }
this.pageDb = new Map();
this.inFlightDb = new Map();
this.pageDataDb = new Map();
this.prefetchTriggered = new Set();
this.prefetchCompleted = new Set();
this.loadComponent = loadComponent;
(0, _findPath.setMatchPaths)(matchPaths);
}
setApiRunner(apiRunner) {
this.apiRunner = apiRunner;
this.prefetchDisabled = apiRunner(`disableCorePrefetching`).some(a => a);
}
loadPageDataJson(rawPath) {
const pagePath = (0, _findPath.findPath)(rawPath);
if (this.pageDataDb.has(pagePath)) {
return Promise.resolve(this.pageDataDb.get(pagePath));
}
return loadPageDataJson({
pagePath
}).then(pageData => {
this.pageDataDb.set(pagePath, pageData);
return pageData;
});
}
findMatchPath(rawPath) {
return (0, _findPath.findMatchPath)(rawPath);
} // TODO check all uses of this and whether they use undefined for page resources not exist
loadPage(rawPath) {
const pagePath = (0, _findPath.findPath)(rawPath);
if (this.pageDb.has(pagePath)) {
const page = this.pageDb.get(pagePath);
return Promise.resolve(page.payload);
}
if (this.inFlightDb.has(pagePath)) {
return this.inFlightDb.get(pagePath);
}
const inFlight = Promise.all([this.loadAppData(), this.loadPageDataJson(pagePath)]).then(allData => {
const result = allData[1];
if (result.status === `error`) {
return {
status: `error`
};
}
if (result.status === `failure`) {
// throw an error so error trackers can pick this up
throw new Error(`404 page could not be found. Checkout https://www.gatsbyjs.org/docs/add-404-page/`);
}
let pageData = result.payload;
const {
componentChunkName
} = pageData;
return this.loadComponent(componentChunkName).then(component => {
const finalResult = {
createdAt: new Date()
};
let pageResources;
if (!component) {
finalResult.status = `error`;
} else {
finalResult.status = `success`;
if (result.notFound === true) {
finalResult.notFound = true;
}
pageData = Object.assign(pageData, {
webpackCompilationHash: allData[0] ? allData[0].webpackCompilationHash : ``
});
pageResources = toPageResources(pageData, component);
finalResult.payload = pageResources;
_emitter.default.emit(`onPostLoadPageResources`, {
page: pageResources,
pageResources
});
}
this.pageDb.set(pagePath, finalResult); // undefined if final result is an error
return pageResources;
});
}) // prefer duplication with then + catch over .finally to prevent problems in ie11 + firefox
.then(response => {
this.inFlightDb.delete(pagePath);
return response;
}).catch(err => {
this.inFlightDb.delete(pagePath);
throw err;
});
this.inFlightDb.set(pagePath, inFlight);
return inFlight;
} // returns undefined if loading page ran into errors
loadPageSync(rawPath) {
const pagePath = (0, _findPath.findPath)(rawPath);
if (this.pageDb.has(pagePath)) {
return this.pageDb.get(pagePath).payload;
}
return undefined;
}
shouldPrefetch(pagePath) {
// Skip prefetching if we know user is on slow or constrained connection
if (!doesConnectionSupportPrefetch()) {
return false;
} // Check if the page exists.
if (this.pageDb.has(pagePath)) {
return false;
}
return true;
}
prefetch(pagePath) {
if (!this.shouldPrefetch(pagePath)) {
return false;
} // Tell plugins with custom prefetching logic that they should start
// prefetching this path.
if (!this.prefetchTriggered.has(pagePath)) {
this.apiRunner(`onPrefetchPathname`, {
pathname: pagePath
});
this.prefetchTriggered.add(pagePath);
} // If a plugin has disabled core prefetching, stop now.
if (this.prefetchDisabled) {
return false;
}
const realPath = (0, _findPath.findPath)(pagePath); // Todo make doPrefetch logic cacheable
// eslint-disable-next-line consistent-return
this.doPrefetch(realPath).then(() => {
if (!this.prefetchCompleted.has(pagePath)) {
this.apiRunner(`onPostPrefetchPathname`, {
pathname: pagePath
});
this.prefetchCompleted.add(pagePath);
}
});
return true;
}
doPrefetch(pagePath) {
throw new Error(`doPrefetch not implemented`);
}
hovering(rawPath) {
this.loadPage(rawPath);
}
getResourceURLsForPathname(rawPath) {
const pagePath = (0, _findPath.findPath)(rawPath);
const page = this.pageDataDb.get(pagePath);
if (page) {
const pageResources = toPageResources(page.payload);
return [...createComponentUrls(pageResources.page.componentChunkName), createPageDataUrl(pagePath)];
} else {
return null;
}
}
isPageNotFound(rawPath) {
const pagePath = (0, _findPath.findPath)(rawPath);
const page = this.pageDb.get(pagePath);
return page && page.notFound === true;
}
loadAppData(retries = 0) {
return doFetch(`${__PATH_PREFIX__}/page-data/app-data.json`).then(req => {
const {
status,
responseText
} = req;
let appData;
if (status !== 200 && retries < 3) {
// Retry 3 times incase of failures
return this.loadAppData(retries + 1);
} // Handle 200
if (status === 200) {
try {
const jsonPayload = JSON.parse(responseText);
if (jsonPayload.webpackCompilationHash === undefined) {
throw new Error(`not a valid app-data response`);
}
appData = jsonPayload;
} catch (err) {// continue regardless of error
}
}
return appData;
});
}
}
exports.BaseLoader = BaseLoader;
const createComponentUrls = componentChunkName => window.___chunkMapping[componentChunkName].map(chunk => __PATH_PREFIX__ + chunk);
class ProdLoader extends BaseLoader {
constructor(asyncRequires, matchPaths) {
const loadComponent = chunkName => asyncRequires.components[chunkName]().then(preferDefault);
super(loadComponent, matchPaths);
}
doPrefetch(pagePath) {
const pageDataUrl = createPageDataUrl(pagePath);
return (0, _prefetch.default)(pageDataUrl, {
crossOrigin: `anonymous`,
as: `fetch`
}).then(() => // This was just prefetched, so will return a response from
// the cache instead of making another request to the server
this.loadPageDataJson(pagePath)).then(result => {
if (result.status !== `success`) {
return Promise.resolve();
}
const pageData = result.payload;
const chunkName = pageData.componentChunkName;
const componentUrls = createComponentUrls(chunkName);
return Promise.all(componentUrls.map(_prefetch.default)).then(() => pageData);
});
}
}
exports.ProdLoader = ProdLoader;
let instance;
const setLoader = _loader => {
instance = _loader;
};
exports.setLoader = setLoader;
const publicLoader = {
// Deprecated methods. As far as we're aware, these are only used by
// core gatsby and the offline plugin, however there's a very small
// chance they're called by others.
getResourcesForPathname: rawPath => {
console.warn(`Warning: getResourcesForPathname is deprecated. Use loadPage instead`);
return instance.i.loadPage(rawPath);
},
getResourcesForPathnameSync: rawPath => {
console.warn(`Warning: getResourcesForPathnameSync is deprecated. Use loadPageSync instead`);
return instance.i.loadPageSync(rawPath);
},
enqueue: rawPath => instance.prefetch(rawPath),
// Real methods
getResourceURLsForPathname: rawPath => instance.getResourceURLsForPathname(rawPath),
loadPage: rawPath => instance.loadPage(rawPath),
loadPageSync: rawPath => instance.loadPageSync(rawPath),
prefetch: rawPath => instance.prefetch(rawPath),
isPageNotFound: rawPath => instance.isPageNotFound(rawPath),
hovering: rawPath => instance.hovering(rawPath),
loadAppData: () => instance.loadAppData()
};
exports.publicLoader = publicLoader;
var _default = publicLoader;
exports.default = _default;