UNPKG

8.32 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
6
7const path = require(`path`);
8
9const {
10 store
11} = require(`../redux`);
12
13const fs = require(`fs`);
14
15const pageDataUtil = require(`../utils/page-data`);
16
17const normalizePagePath = require(`../utils/normalize-page-path`);
18
19const telemetry = require(`gatsby-telemetry`);
20
21const url = require(`url`);
22
23const {
24 createHash
25} = require(`crypto`);
26
27const denormalize = path => {
28 if (path === undefined) {
29 return path;
30 }
31
32 if (path === `/`) {
33 return `/`;
34 }
35
36 if (path.charAt(path.length - 1) !== `/`) {
37 return path + `/`;
38 }
39
40 return path;
41};
42/**
43 * Get cached page query result for given page path.
44 * @param {string} pagePath Path to a page.
45 * @param {string} directory Root directory of current project.
46 */
47
48
49const getCachedPageData = async (pagePath, directory) => {
50 const {
51 program,
52 pages
53 } = store.getState();
54 const publicDir = path.join(program.directory, `public`);
55
56 if (pages.has(denormalize(pagePath)) || pages.has(pagePath)) {
57 try {
58 const pageData = await pageDataUtil.read({
59 publicDir
60 }, pagePath);
61 return {
62 result: pageData.result,
63 id: pagePath
64 };
65 } catch (err) {
66 throw new Error(`Error loading a result for the page query in "${pagePath}". Query was not run and no cached result was found.`);
67 }
68 }
69
70 return undefined;
71};
72
73const hashPaths = paths => {
74 if (!paths) {
75 return undefined;
76 }
77
78 return paths.map(path => {
79 if (!path) {
80 return undefined;
81 }
82
83 return createHash(`sha256`).update(path).digest(`hex`);
84 });
85};
86/**
87 * Get cached StaticQuery results for components that Gatsby didn't run query yet.
88 * @param {QueryResultsMap} resultsMap Already stored results for queries that don't need to be read from files.
89 * @param {string} directory Root directory of current project.
90 */
91
92
93const getCachedStaticQueryResults = (resultsMap, directory) => {
94 const cachedStaticQueryResults = new Map();
95 const {
96 staticQueryComponents
97 } = store.getState();
98 staticQueryComponents.forEach(staticQueryComponent => {
99 // Don't read from file if results were already passed from query runner
100 if (resultsMap.has(staticQueryComponent.hash)) return;
101 const filePath = path.join(directory, `public`, `static`, `d`, `${staticQueryComponent.hash}.json`);
102 const fileResult = fs.readFileSync(filePath, `utf-8`);
103
104 if (fileResult === `undefined`) {
105 console.log(`Error loading a result for the StaticQuery in "${staticQueryComponent.componentPath}". Query was not run and no cached result was found.`);
106 return;
107 }
108
109 cachedStaticQueryResults.set(staticQueryComponent.hash, {
110 result: JSON.parse(fileResult),
111 id: staticQueryComponent.hash
112 });
113 });
114 return cachedStaticQueryResults;
115};
116
117const getRoomNameFromPath = path => `path-${path}`;
118
119class WebsocketManager {
120 constructor() {
121 (0, _defineProperty2.default)(this, "pageResults", void 0);
122 (0, _defineProperty2.default)(this, "staticQueryResults", void 0);
123 (0, _defineProperty2.default)(this, "errors", void 0);
124 (0, _defineProperty2.default)(this, "isInitialised", void 0);
125 (0, _defineProperty2.default)(this, "activePaths", void 0);
126 (0, _defineProperty2.default)(this, "programDir", void 0);
127 this.isInitialised = false;
128 this.activePaths = new Set();
129 this.pageResults = new Map();
130 this.staticQueryResults = new Map();
131 this.errors = new Map(); // this.websocket
132 // this.programDir
133
134 this.init = this.init.bind(this);
135 this.getSocket = this.getSocket.bind(this);
136 this.emitPageData = this.emitPageData.bind(this);
137 this.emitStaticQueryData = this.emitStaticQueryData.bind(this);
138 this.emitError = this.emitError.bind(this);
139 this.connectedClients = 0;
140 }
141
142 init({
143 server,
144 directory
145 }) {
146 this.programDir = directory;
147 const cachedStaticQueryResults = getCachedStaticQueryResults(this.staticQueryResults, this.programDir);
148 this.staticQueryResults = new Map([...this.staticQueryResults, ...cachedStaticQueryResults]);
149 this.websocket = require(`socket.io`)(server);
150 this.websocket.on(`connection`, s => {
151 let activePath = null;
152
153 if (s && s.handshake && s.handshake.headers && s.handshake.headers.referer) {
154 const path = url.parse(s.handshake.headers.referer).path;
155
156 if (path) {
157 activePath = path;
158 this.activePaths.add(path);
159 }
160 }
161
162 this.connectedClients += 1; // Send already existing static query results
163
164 this.staticQueryResults.forEach(result => {
165 this.websocket.send({
166 type: `staticQueryResult`,
167 payload: result
168 });
169 });
170 this.errors.forEach((message, errorID) => {
171 this.websocket.send({
172 type: `overlayError`,
173 payload: {
174 id: errorID,
175 message
176 }
177 });
178 });
179
180 const leaveRoom = path => {
181 s.leave(getRoomNameFromPath(path));
182 const leftRoom = this.websocket.sockets.adapter.rooms[getRoomNameFromPath(path)];
183
184 if (!leftRoom || leftRoom.length === 0) {
185 this.activePaths.delete(path);
186 }
187 };
188
189 const getDataForPath = async path => {
190 if (!this.pageResults.has(path)) {
191 try {
192 const result = await getCachedPageData(path, this.programDir);
193 this.pageResults.set(path, result || {
194 id: path
195 });
196 } catch (err) {
197 console.log(err.message);
198 return;
199 }
200 }
201
202 this.websocket.send({
203 type: `pageQueryResult`,
204 why: `getDataForPath`,
205 payload: this.pageResults.get(path)
206 });
207 const clientsCount = this.connectedClients;
208
209 if (clientsCount && clientsCount > 0) {
210 telemetry.trackCli(`WEBSOCKET_PAGE_DATA_UPDATE`, {
211 siteMeasurements: {
212 clientsCount,
213 paths: hashPaths(Array.from(this.activePaths))
214 }
215 }, {
216 debounce: true
217 });
218 }
219 };
220
221 s.on(`getDataForPath`, getDataForPath);
222 s.on(`registerPath`, path => {
223 s.join(getRoomNameFromPath(path));
224 activePath = path;
225 this.activePaths.add(path);
226 });
227 s.on(`disconnect`, s => {
228 leaveRoom(activePath);
229 this.connectedClients -= 1;
230 });
231 s.on(`unregisterPath`, path => {
232 leaveRoom(path);
233 });
234 });
235 this.isInitialised = true;
236 }
237
238 getSocket() {
239 return this.isInitialised && this.websocket;
240 }
241
242 emitStaticQueryData(data) {
243 this.staticQueryResults.set(data.id, data);
244
245 if (this.isInitialised) {
246 this.websocket.send({
247 type: `staticQueryResult`,
248 payload: data
249 });
250 const clientsCount = this.connectedClients;
251
252 if (clientsCount && clientsCount > 0) {
253 telemetry.trackCli(`WEBSOCKET_EMIT_STATIC_PAGE_DATA_UPDATE`, {
254 siteMeasurements: {
255 clientsCount,
256 paths: hashPaths(Array.from(this.activePaths))
257 }
258 }, {
259 debounce: true
260 });
261 }
262 }
263 }
264
265 emitPageData(data) {
266 data.id = normalizePagePath(data.id);
267 this.pageResults.set(data.id, data);
268
269 if (this.isInitialised) {
270 this.websocket.send({
271 type: `pageQueryResult`,
272 payload: data
273 });
274 const clientsCount = this.connectedClients;
275
276 if (clientsCount && clientsCount > 0) {
277 telemetry.trackCli(`WEBSOCKET_EMIT_PAGE_DATA_UPDATE`, {
278 siteMeasurements: {
279 clientsCount,
280 paths: hashPaths(Array.from(this.activePaths))
281 }
282 }, {
283 debounce: true
284 });
285 }
286 }
287 }
288
289 emitError(id, message) {
290 if (message) {
291 this.errors.set(id, message);
292 } else {
293 this.errors.delete(id);
294 }
295
296 if (this.isInitialised) {
297 this.websocket.send({
298 type: `overlayError`,
299 payload: {
300 id,
301 message
302 }
303 });
304 }
305 }
306
307}
308
309const manager = new WebsocketManager();
310module.exports = manager;
311//# sourceMappingURL=websocket-manager.js.map
\No newline at end of file