314 lines
8.1 KiB
JavaScript
314 lines
8.1 KiB
JavaScript
"use strict";
|
|
|
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
|
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
|
|
const path = require(`path`);
|
|
|
|
const {
|
|
store
|
|
} = require(`../redux`);
|
|
|
|
const fs = require(`fs`);
|
|
|
|
const pageDataUtil = require(`../utils/page-data`);
|
|
|
|
const normalizePagePath = require(`../utils/normalize-page-path`);
|
|
|
|
const telemetry = require(`gatsby-telemetry`);
|
|
|
|
const url = require(`url`);
|
|
|
|
const {
|
|
createHash
|
|
} = require(`crypto`);
|
|
|
|
const denormalize = path => {
|
|
if (path === undefined) {
|
|
return path;
|
|
}
|
|
|
|
if (path === `/`) {
|
|
return `/`;
|
|
}
|
|
|
|
if (path.charAt(path.length - 1) !== `/`) {
|
|
return path + `/`;
|
|
}
|
|
|
|
return path;
|
|
};
|
|
/**
|
|
* Get cached page query result for given page path.
|
|
* @param {string} pagePath Path to a page.
|
|
* @param {string} directory Root directory of current project.
|
|
*/
|
|
|
|
|
|
const getCachedPageData = async (pagePath, directory) => {
|
|
const {
|
|
program,
|
|
pages
|
|
} = store.getState();
|
|
const publicDir = path.join(program.directory, `public`);
|
|
|
|
if (pages.has(denormalize(pagePath)) || pages.has(pagePath)) {
|
|
try {
|
|
const pageData = await pageDataUtil.read({
|
|
publicDir
|
|
}, pagePath);
|
|
return {
|
|
result: pageData.result,
|
|
id: pagePath
|
|
};
|
|
} catch (err) {
|
|
throw new Error(`Error loading a result for the page query in "${pagePath}". Query was not run and no cached result was found.`);
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
};
|
|
|
|
const hashPaths = paths => {
|
|
if (!paths) {
|
|
return undefined;
|
|
}
|
|
|
|
return paths.map(path => {
|
|
if (!path) {
|
|
return undefined;
|
|
}
|
|
|
|
return createHash(`sha256`).update(path).digest(`hex`);
|
|
});
|
|
};
|
|
/**
|
|
* Get cached StaticQuery results for components that Gatsby didn't run query yet.
|
|
* @param {QueryResultsMap} resultsMap Already stored results for queries that don't need to be read from files.
|
|
* @param {string} directory Root directory of current project.
|
|
*/
|
|
|
|
|
|
const getCachedStaticQueryResults = (resultsMap, directory) => {
|
|
const cachedStaticQueryResults = new Map();
|
|
const {
|
|
staticQueryComponents
|
|
} = store.getState();
|
|
staticQueryComponents.forEach(staticQueryComponent => {
|
|
// Don't read from file if results were already passed from query runner
|
|
if (resultsMap.has(staticQueryComponent.hash)) return;
|
|
const filePath = path.join(directory, `public`, `static`, `d`, `${staticQueryComponent.hash}.json`);
|
|
const fileResult = fs.readFileSync(filePath, `utf-8`);
|
|
|
|
if (fileResult === `undefined`) {
|
|
console.log(`Error loading a result for the StaticQuery in "${staticQueryComponent.componentPath}". Query was not run and no cached result was found.`);
|
|
return;
|
|
}
|
|
|
|
cachedStaticQueryResults.set(staticQueryComponent.hash, {
|
|
result: JSON.parse(fileResult),
|
|
id: staticQueryComponent.hash
|
|
});
|
|
});
|
|
return cachedStaticQueryResults;
|
|
};
|
|
|
|
const getRoomNameFromPath = path => `path-${path}`;
|
|
|
|
class WebsocketManager {
|
|
constructor() {
|
|
(0, _defineProperty2.default)(this, "pageResults", void 0);
|
|
(0, _defineProperty2.default)(this, "staticQueryResults", void 0);
|
|
(0, _defineProperty2.default)(this, "errors", void 0);
|
|
(0, _defineProperty2.default)(this, "isInitialised", void 0);
|
|
(0, _defineProperty2.default)(this, "activePaths", void 0);
|
|
(0, _defineProperty2.default)(this, "programDir", void 0);
|
|
this.isInitialised = false;
|
|
this.activePaths = new Set();
|
|
this.pageResults = new Map();
|
|
this.staticQueryResults = new Map();
|
|
this.errors = new Map(); // this.websocket
|
|
// this.programDir
|
|
|
|
this.init = this.init.bind(this);
|
|
this.getSocket = this.getSocket.bind(this);
|
|
this.emitPageData = this.emitPageData.bind(this);
|
|
this.emitStaticQueryData = this.emitStaticQueryData.bind(this);
|
|
this.emitError = this.emitError.bind(this);
|
|
this.connectedClients = 0;
|
|
}
|
|
|
|
init({
|
|
server,
|
|
directory
|
|
}) {
|
|
this.programDir = directory;
|
|
const cachedStaticQueryResults = getCachedStaticQueryResults(this.staticQueryResults, this.programDir);
|
|
this.staticQueryResults = new Map([...this.staticQueryResults, ...cachedStaticQueryResults]);
|
|
this.websocket = require(`socket.io`)(server);
|
|
this.websocket.on(`connection`, s => {
|
|
let activePath = null;
|
|
|
|
if (s && s.handshake && s.handshake.headers && s.handshake.headers.referer) {
|
|
const path = url.parse(s.handshake.headers.referer).path;
|
|
|
|
if (path) {
|
|
activePath = path;
|
|
this.activePaths.add(path);
|
|
}
|
|
}
|
|
|
|
this.connectedClients += 1; // Send already existing static query results
|
|
|
|
this.staticQueryResults.forEach(result => {
|
|
this.websocket.send({
|
|
type: `staticQueryResult`,
|
|
payload: result
|
|
});
|
|
});
|
|
this.errors.forEach((message, errorID) => {
|
|
this.websocket.send({
|
|
type: `overlayError`,
|
|
payload: {
|
|
id: errorID,
|
|
message
|
|
}
|
|
});
|
|
});
|
|
|
|
const leaveRoom = path => {
|
|
s.leave(getRoomNameFromPath(path));
|
|
const leftRoom = this.websocket.sockets.adapter.rooms[getRoomNameFromPath(path)];
|
|
|
|
if (!leftRoom || leftRoom.length === 0) {
|
|
this.activePaths.delete(path);
|
|
}
|
|
};
|
|
|
|
const getDataForPath = async path => {
|
|
if (!this.pageResults.has(path)) {
|
|
try {
|
|
const result = await getCachedPageData(path, this.programDir);
|
|
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
this.pageResults.set(path, result);
|
|
} catch (err) {
|
|
console.log(err.message);
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.websocket.send({
|
|
type: `pageQueryResult`,
|
|
why: `getDataForPath`,
|
|
payload: this.pageResults.get(path)
|
|
});
|
|
const clientsCount = this.connectedClients;
|
|
|
|
if (clientsCount && clientsCount > 0) {
|
|
telemetry.trackCli(`WEBSOCKET_PAGE_DATA_UPDATE`, {
|
|
siteMeasurements: {
|
|
clientsCount,
|
|
paths: hashPaths(Array.from(this.activePaths))
|
|
}
|
|
}, {
|
|
debounce: true
|
|
});
|
|
}
|
|
};
|
|
|
|
s.on(`getDataForPath`, getDataForPath);
|
|
s.on(`registerPath`, path => {
|
|
s.join(getRoomNameFromPath(path));
|
|
activePath = path;
|
|
this.activePaths.add(path);
|
|
});
|
|
s.on(`disconnect`, s => {
|
|
leaveRoom(activePath);
|
|
this.connectedClients -= 1;
|
|
});
|
|
s.on(`unregisterPath`, path => {
|
|
leaveRoom(path);
|
|
});
|
|
});
|
|
this.isInitialised = true;
|
|
}
|
|
|
|
getSocket() {
|
|
return this.isInitialised && this.websocket;
|
|
}
|
|
|
|
emitStaticQueryData(data) {
|
|
this.staticQueryResults.set(data.id, data);
|
|
|
|
if (this.isInitialised) {
|
|
this.websocket.send({
|
|
type: `staticQueryResult`,
|
|
payload: data
|
|
});
|
|
const clientsCount = this.connectedClients;
|
|
|
|
if (clientsCount && clientsCount > 0) {
|
|
telemetry.trackCli(`WEBSOCKET_EMIT_STATIC_PAGE_DATA_UPDATE`, {
|
|
siteMeasurements: {
|
|
clientsCount,
|
|
paths: hashPaths(Array.from(this.activePaths))
|
|
}
|
|
}, {
|
|
debounce: true
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
emitPageData(data) {
|
|
data.id = normalizePagePath(data.id);
|
|
this.pageResults.set(data.id, data);
|
|
|
|
if (this.isInitialised) {
|
|
this.websocket.send({
|
|
type: `pageQueryResult`,
|
|
payload: data
|
|
});
|
|
const clientsCount = this.connectedClients;
|
|
|
|
if (clientsCount && clientsCount > 0) {
|
|
telemetry.trackCli(`WEBSOCKET_EMIT_PAGE_DATA_UPDATE`, {
|
|
siteMeasurements: {
|
|
clientsCount,
|
|
paths: hashPaths(Array.from(this.activePaths))
|
|
}
|
|
}, {
|
|
debounce: true
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
emitError(id, message) {
|
|
if (message) {
|
|
this.errors.set(id, message);
|
|
} else {
|
|
this.errors.delete(id);
|
|
}
|
|
|
|
if (this.isInitialised) {
|
|
this.websocket.send({
|
|
type: `overlayError`,
|
|
payload: {
|
|
id,
|
|
message
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
const manager = new WebsocketManager();
|
|
module.exports = manager;
|
|
//# sourceMappingURL=websocket-manager.js.map
|