diff --git a/packages/gatsby/cache-dir/json-store.js b/packages/gatsby/cache-dir/json-store.js index 243ec4d041086..3b2ca94121b23 100644 --- a/packages/gatsby/cache-dir/json-store.js +++ b/packages/gatsby/cache-dir/json-store.js @@ -6,81 +6,89 @@ import socketIo from "./socketIo" import omit from "lodash/omit" import get from "lodash/get" +const getPathFromProps = props => { + if (props.isPage) { + return get(props.pageResources, `page.path`) + } else { + return `/dev-404-page/` + } +} + class JSONStore extends React.Component { constructor(props) { super(props) this.state = { data: {}, + path: null, } - this.socket = socketIo() - this.setPageData = this.setPageData.bind(this) - this.getPageData = this.getPageData.bind(this) - } - componentDidMount() { + this.socket = socketIo() this.socket.on(`queryResult`, this.setPageData) } - shouldComponentUpdate(nextProps, nextState) { - if (nextProps !== this.props) return true - - // if json for nextState is not available - const nextJsonId = get(nextProps.pageResources, `page.jsonName`) - if (!nextState.data[nextJsonId]) return false - - // if nextState json is the same as current state json - const sameDataPath = - get(nextState, `data[${nextJsonId}].dataPath`) === - get(this, `state.data[${nextJsonId}].dataPath`) + componentWillMount() { + this.registerPath(getPathFromProps(this.props)) + } - if (sameDataPath) return false + componentWillReceiveProps(nextProps) { + const { path } = this.state + const newPath = getPathFromProps(nextProps) - return true + if (path !== newPath) { + this.unregisterPath(path) + this.registerPath(newPath) + } } - setPageData(newData) { - this.setState({ - data: { [newData.path]: newData }, - }) + registerPath(path) { + this.setState({ path }) + this.socket.emit(`registerPath`, path) } - getPageData(path) { - const res = this.state.data[path] + unregisterPath(path) { + this.setState({ path: null }) + this.socket.emit(`unregisterPath`, path) + } - // always check for fresh data - this.socket.emit(`getPageData`, path) + componentWillUnmount() { + this.unregisterPath(this.state.path) + } - if (!res || !res.data) return false - return JSON.parse(res.data) + setPageData({ path, result }) { + this.setState({ + data: { + ...this.state.data, + [path]: result, + }, + }) } render() { const { isPage, pages, pageResources } = this.props + const data = this.state.data[this.state.path] const propsWithoutPages = omit(this.props, `pages`) - if (isPage) { - const jsonId = get(pageResources, `page.jsonName`) - const pageData = this.getPageData(jsonId) - if (pageData === false) return `` + if (!data) { + return null + } else if (isPage) { return createElement(ComponentRenderer, { key: `normal-page`, ...propsWithoutPages, - ...pageData, + ...data, }) } else { const dev404Page = pages.find(p => /^\/dev-404-page/.test(p.path)) - const dev404Props = { - ...propsWithoutPages, - ...this.getPageData(dev404Page.jsonName), - } return createElement(Route, { key: `404-page`, component: props => createElement( syncRequires.components[dev404Page.componentChunkName], - dev404Props + { + ...propsWithoutPages, + ...data, + } ), }) } diff --git a/packages/gatsby/src/internal-plugins/query-runner/query-runner.js b/packages/gatsby/src/internal-plugins/query-runner/query-runner.js index 1f3e35dc42c8e..d130bda7c7472 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/query-runner.js +++ b/packages/gatsby/src/internal-plugins/query-runner/query-runner.js @@ -70,13 +70,11 @@ module.exports = async (pageOrLayout, component) => { const programType = program._[0] - if (programType === `develop`) { - const data = { - dataPath, - data: resultJSON, - path: pageOrLayout.jsonName, - } - websocketManager.emitData({ data }) + if (programType === `develop` && pageOrLayout.path) { + websocketManager.emitData({ + result, + path: pageOrLayout.path, + }) } const resultPath = path.join( diff --git a/packages/gatsby/src/utils/websocket-manager.js b/packages/gatsby/src/utils/websocket-manager.js index 509366cbf9942..072f1917ec489 100644 --- a/packages/gatsby/src/utils/websocket-manager.js +++ b/packages/gatsby/src/utils/websocket-manager.js @@ -2,9 +2,10 @@ const path = require(`path`) const { store } = require(`../redux`) const fs = require(`fs`) -const getCachedPageData = (jsonName, directory) => { - const jsonDataPaths = store.getState().jsonDataPaths - const dataPath = jsonDataPaths[jsonName] +const getCachedPageData = (pagePath, directory) => { + const { jsonDataPaths, pages } = store.getState() + const page = pages.find(p => p.path === pagePath) + const dataPath = jsonDataPaths[page.jsonName] if (typeof dataPath === `undefined`) return undefined const filePath = path.join( directory, @@ -13,25 +14,25 @@ const getCachedPageData = (jsonName, directory) => { `d`, `${dataPath}.json` ) - const data = fs.readFileSync(filePath, `utf-8`) + const result = JSON.parse(fs.readFileSync(filePath, `utf-8`)) return { - data, - dataPath, - path: jsonName, + result, + path: pagePath, } } +const getRoomNameFromPath = path => `path-${path}` + class WebsocketManager { constructor() { this.isInitialised = false - this.results = {} this.activePaths = new Map() + this.results = new Map() this.websocket this.programDir this.init = this.init.bind(this) this.getSocket = this.getSocket.bind(this) - this.getPageData = this.getPageData.bind(this) this.emitData = this.emitData.bind(this) } @@ -40,13 +41,33 @@ class WebsocketManager { this.websocket = require(`socket.io`)(server) this.websocket.on(`connection`, s => { - const clientId = s.id - s.join(`clients`) - s.on(`getPageData`, path => { - this.getPageData(path, clientId) - }) + let activePath = null + + const leaveRoom = path => { + s.leave(getRoomNameFromPath(path)) + activePath = null + } + s.on(`disconnect`, s => { - this.activePaths.delete(clientId) + leaveRoom(activePath) + }) + + s.on(`registerPath`, path => { + s.join(getRoomNameFromPath(path)) + activePath = path + this.activePaths.set(s.id, path) + + if (this.results.has(path)) { + s.emit(`queryResult`, this.results.get(path)) + } else { + const result = getCachedPageData(path, this.programDir) + this.results.set(path, result) + s.emit(`queryResult`, result) + } + }) + + s.on(`unregisterPath`, path => { + leaveRoom(path) }) }) @@ -57,21 +78,16 @@ class WebsocketManager { return this.isInitialised && this.websocket } - getPageData(path, clientId) { - // track the active path for each connected client - this.activePaths.set(clientId, path) - const data = getCachedPageData(path, this.programDir) - this.emitData({ data, forceEmit: true }) - } - - emitData({ data, forceEmit = false }) { + emitData(data) { const isActivePath = data.path && Array.from(this.activePaths.values()).includes(data.path) - // push results if this path is active on a client - if (isActivePath || forceEmit) { - this.websocket.emit(`queryResult`, data) + if (isActivePath) { + this.websocket + .to(getRoomNameFromPath(data.path)) + .emit(`queryResult`, data) } + this.results.set(data.path, data) } }