| @@ -0,0 +1,41 @@ | ||
| var plugins = [{ | ||
| plugin: require('/Users/jonathanbry/LambdaSchool/TruckingWebsite/node_modules/gatsby-plugin-react-helmet/gatsby-ssr.js'), | ||
| options: {"plugins":[]}, | ||
| }] | ||
| // During bootstrap, we write requires at top of this file which looks like: | ||
| // var plugins = [ | ||
| // { | ||
| // plugin: require("/path/to/plugin1/gatsby-ssr.js"), | ||
| // options: { ... }, | ||
| // }, | ||
| // { | ||
| // plugin: require("/path/to/plugin2/gatsby-ssr.js"), | ||
| // options: { ... }, | ||
| // }, | ||
| // ] | ||
|
|
||
| const apis = require(`./api-ssr-docs`) | ||
|
|
||
| // Run the specified API in any plugins that have implemented it | ||
| module.exports = (api, args, defaultReturn) => { | ||
| if (!apis[api]) { | ||
| console.log(`This API doesn't exist`, api) | ||
| } | ||
|
|
||
| // Run each plugin in series. | ||
| let results = plugins.map(plugin => { | ||
| if (plugin.plugin[api]) { | ||
| const result = plugin.plugin[api](args, plugin.options) | ||
| return result | ||
| } | ||
| }) | ||
|
|
||
| // Filter out undefined results. | ||
| results = results.filter(result => typeof result !== `undefined`) | ||
|
|
||
| if (results.length > 0) { | ||
| return results | ||
| } else { | ||
| return [defaultReturn] | ||
| } | ||
| } |
| @@ -0,0 +1,95 @@ | ||
| /** | ||
| * Replace the default server renderer. This is useful for integration with | ||
| * Redux, css-in-js libraries, etc. that need custom setups for server | ||
| * rendering. | ||
| * @param {Object} $0 | ||
| * @param {function} $0.replaceBodyHTMLString Call this with the HTML string | ||
| * you render. **WARNING** if multiple plugins implement this API it's the | ||
| * last plugin that "wins". TODO implement an automated warning against this. | ||
| * @param {function} $0.setHeadComponents Takes an array of components as its | ||
| * first argument which are added to the `headComponents` array which is passed | ||
| * to the `html.js` component. | ||
| * @param {function} $0.setHtmlAttributes Takes an object of props which will | ||
| * spread into the `<html>` component. | ||
| * @param {function} $0.setBodyAttributes Takes an object of props which will | ||
| * spread into the `<body>` component. | ||
| * @param {function} $0.setPreBodyComponents Takes an array of components as its | ||
| * first argument which are added to the `preBodyComponents` array which is passed | ||
| * to the `html.js` component. | ||
| * @param {function} $0.setPostBodyComponents Takes an array of components as its | ||
| * first argument which are added to the `postBodyComponents` array which is passed | ||
| * to the `html.js` component. | ||
| * @param {function} $0.setBodyProps Takes an object of data which | ||
| * is merged with other body props and passed to `html.js` as `bodyProps`. | ||
| * @param {Object} pluginOptions | ||
| * @example | ||
| * // From gatsby-plugin-glamor | ||
| * import { renderToString } from "react-dom/server" | ||
| * import inline from "glamor-inline" | ||
| * | ||
| * exports.replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => { | ||
| * const bodyHTML = renderToString(bodyComponent) | ||
| * const inlinedHTML = inline(bodyHTML) | ||
| * | ||
| * replaceBodyHTMLString(inlinedHTML) | ||
| * } | ||
| */ | ||
| exports.replaceRenderer = true | ||
|
|
||
| /** | ||
| * Called after every page Gatsby server renders while building HTML so you can | ||
| * set head and body components to be rendered in your `html.js`. | ||
| * | ||
| * Gatsby does a two-pass render for HTML. It loops through your pages first | ||
| * rendering only the body and then takes the result body HTML string and | ||
| * passes it as the `body` prop to your `html.js` to complete the render. | ||
| * | ||
| * It's often handy to be able to send custom components to your `html.js`. | ||
| * For example, it's a very common pattern for React.js libraries that | ||
| * support server rendering to pull out data generated during the render to | ||
| * add to your HTML. | ||
| * | ||
| * Using this API over [`replaceRenderer`](#replaceRenderer) is preferable as | ||
| * multiple plugins can implement this API where only one plugin can take | ||
| * over server rendering. However, if your plugin requires taking over server | ||
| * rendering then that's the one to | ||
| * use | ||
| * @param {Object} $0 | ||
| * @param {string} $0.pathname The pathname of the page currently being rendered. | ||
| * @param {function} $0.setHeadComponents Takes an array of components as its | ||
| * first argument which are added to the `headComponents` array which is passed | ||
| * to the `html.js` component. | ||
| * @param {function} $0.setHtmlAttributes Takes an object of props which will | ||
| * spread into the `<html>` component. | ||
| * @param {function} $0.setBodyAttributes Takes an object of props which will | ||
| * spread into the `<body>` component. | ||
| * @param {function} $0.setPreBodyComponents Takes an array of components as its | ||
| * first argument which are added to the `preBodyComponents` array which is passed | ||
| * to the `html.js` component. | ||
| * @param {function} $0.setPostBodyComponents Takes an array of components as its | ||
| * first argument which are added to the `postBodyComponents` array which is passed | ||
| * to the `html.js` component. | ||
| * @param {function} $0.setBodyProps Takes an object of data which | ||
| * is merged with other body props and passed to `html.js` as `bodyProps`. | ||
| * @param {Object} pluginOptions | ||
| * @example | ||
| * import Helmet from "react-helmet" | ||
| * | ||
| * exports.onRenderBody = ( | ||
| * { setHeadComponents, setHtmlAttributes, setBodyAttributes }, | ||
| * pluginOptions | ||
| * ) => { | ||
| * const helmet = Helmet.renderStatic() | ||
| * setHtmlAttributes(helmet.htmlAttributes.toComponent()) | ||
| * setBodyAttributes(helmet.bodyAttributes.toComponent()) | ||
| * setHeadComponents([ | ||
| * helmet.title.toComponent(), | ||
| * helmet.link.toComponent(), | ||
| * helmet.meta.toComponent(), | ||
| * helmet.noscript.toComponent(), | ||
| * helmet.script.toComponent(), | ||
| * helmet.style.toComponent(), | ||
| * ]) | ||
| * } | ||
| */ | ||
| exports.onRenderBody = true |
| @@ -0,0 +1,78 @@ | ||
| if (__POLYFILL__) { | ||
| require(`core-js/modules/es6.promise`) | ||
| } | ||
| import React from "react" | ||
| import ReactDOM from "react-dom" | ||
| import { AppContainer as HotContainer } from "react-hot-loader" | ||
| import domReady from "domready" | ||
|
|
||
| import socketIo from "./socketIo" | ||
| import { apiRunner, apiRunnerAsync } from "./api-runner-browser" | ||
|
|
||
| window.___emitter = require(`./emitter`) | ||
|
|
||
| // Let the site/plugins run code very early. | ||
| apiRunnerAsync(`onClientEntry`).then(() => { | ||
| // Hook up the client to socket.io on server | ||
| socketIo() | ||
|
|
||
| /** | ||
| * Service Workers are persistent by nature. They stick around, | ||
| * serving a cached version of the site if they aren't removed. | ||
| * This is especially frustrating when you need to test the | ||
| * production build on your local machine. | ||
| * | ||
| * Let's unregister the service workers in development, and tidy up a few errors. | ||
| */ | ||
| if (supportsServiceWorkers(location, navigator)) { | ||
| navigator.serviceWorker.getRegistrations().then(registrations => { | ||
| for (let registration of registrations) { | ||
| registration.unregister() | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| const rootElement = document.getElementById(`___gatsby`) | ||
|
|
||
| let Root = require(`./root`) | ||
| if (Root.default) { | ||
| Root = Root.default | ||
| } | ||
|
|
||
| domReady(() => | ||
| ReactDOM.render( | ||
| <HotContainer> | ||
| <Root /> | ||
| </HotContainer>, | ||
| rootElement, | ||
| () => { | ||
| apiRunner(`onInitialClientRender`) | ||
| } | ||
| ) | ||
| ) | ||
|
|
||
| if (module.hot) { | ||
| module.hot.accept(`./root`, () => { | ||
| let NextRoot = require(`./root`) | ||
| if (NextRoot.default) { | ||
| NextRoot = NextRoot.default | ||
| } | ||
| ReactDOM.render( | ||
| <HotContainer> | ||
| <NextRoot /> | ||
| </HotContainer>, | ||
| rootElement, | ||
| () => { | ||
| apiRunner(`onInitialClientRender`) | ||
| } | ||
| ) | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
| function supportsServiceWorkers(location, navigator) { | ||
| if (location.hostname === `localhost` || location.protocol === `https:`) { | ||
| return `serviceWorker` in navigator | ||
| } | ||
| return false | ||
| } |
| @@ -0,0 +1,18 @@ | ||
| // prefer default export if available | ||
| const preferDefault = m => m && m.default || m | ||
|
|
||
| exports.components = { | ||
| "component---themes-photon-pages-index-js": require("gatsby-module-loader?name=component---themes-photon-pages-index-js!/Users/jonathanbry/LambdaSchool/TruckingWebsite/themes/photon/pages/index.js"), | ||
| "component---themes-photon-templates-post-js": require("gatsby-module-loader?name=component---themes-photon-templates-post-js!/Users/jonathanbry/LambdaSchool/TruckingWebsite/themes/photon/templates/post.js"), | ||
| "component---cache-dev-404-page-js": require("gatsby-module-loader?name=component---cache-dev-404-page-js!/Users/jonathanbry/LambdaSchool/TruckingWebsite/.cache/dev-404-page.js") | ||
| } | ||
|
|
||
| exports.json = { | ||
| "index.json": require("gatsby-module-loader?name=path---index!/Users/jonathanbry/LambdaSchool/TruckingWebsite/.cache/json/index.json"), | ||
| "first-post.json": require("gatsby-module-loader?name=path---first-post!/Users/jonathanbry/LambdaSchool/TruckingWebsite/.cache/json/first-post.json"), | ||
| "dev-404-page.json": require("gatsby-module-loader?name=path---dev-404-page!/Users/jonathanbry/LambdaSchool/TruckingWebsite/.cache/json/dev-404-page.json") | ||
| } | ||
|
|
||
| exports.layouts = { | ||
|
|
||
| } |
| @@ -0,0 +1 @@ | ||
| {"keys":[{"id":"transformer-remark-markdown-ast-6faaf3c7800acd7d3e130073b3e4b06f-gatsby-remark-images-","value":{"type":"root","children":[{"type":"yaml","value":"title: \"Magna feugiat lorem\"\npath: \"/first-post\"\ndate: \"2000-03-21\"\nmodalImage: \"./full_01.jpg\"\nthumbnailImage: \"./thumb_01.jpg\"","position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":7,"column":4,"offset":136},"indent":[1,1,1,1,1,1]}},{"type":"paragraph","children":[{"type":"text","value":"Nunc blandit nisi ligula magna sodales lectus elementum non. Integer id venenatis velit.","position":{"start":{"line":9,"column":1,"offset":138},"end":{"line":9,"column":89,"offset":226},"indent":[]}}],"position":{"start":{"line":9,"column":1,"offset":138},"end":{"line":9,"column":89,"offset":226},"indent":[]}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":10,"column":1,"offset":227}}}},{"id":"transformer-remark-markdown-html-ast-6faaf3c7800acd7d3e130073b3e4b06f-gatsby-remark-images-","value":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Nunc blandit nisi ligula magna sodales lectus elementum non. Integer id venenatis velit.","position":{"start":{"line":9,"column":1,"offset":138},"end":{"line":9,"column":89,"offset":226}}}],"position":{"start":{"line":9,"column":1,"offset":138},"end":{"line":9,"column":89,"offset":226}}}],"position":{"start":{"line":1,"column":1,"offset":0},"end":{"line":10,"column":1,"offset":227}}}},{"id":"transformer-remark-markdown-html-6faaf3c7800acd7d3e130073b3e4b06f-gatsby-remark-images-","value":"<p>Nunc blandit nisi ligula magna sodales lectus elementum non. Integer id venenatis velit.</p>"}]} |
| @@ -0,0 +1,171 @@ | ||
| import React, { createElement } from "react" | ||
| import PropTypes from "prop-types" | ||
| import loader, { publicLoader } from "./loader" | ||
| import emitter from "./emitter" | ||
| import { apiRunner } from "./api-runner-browser" | ||
| import shallowCompare from "shallow-compare" | ||
|
|
||
| const DefaultLayout = ({ children }) => <div>{children()}</div> | ||
|
|
||
| // Pass pathname in as prop. | ||
| // component will try fetching resources. If they exist, | ||
| // will just render, else will render null. | ||
| class ComponentRenderer extends React.Component { | ||
| constructor(props) { | ||
| super() | ||
| let location = props.location | ||
|
|
||
| // Set the pathname for 404 pages. | ||
| if (!loader.getPage(location.pathname)) { | ||
| location = Object.assign({}, location, { | ||
| pathname: `/404.html`, | ||
| }) | ||
| } | ||
|
|
||
| this.state = { | ||
| location, | ||
| pageResources: loader.getResourcesForPathname(location.pathname), | ||
| } | ||
| } | ||
|
|
||
| componentWillReceiveProps(nextProps) { | ||
| // During development, always pass a component's JSON through so graphql | ||
| // updates go through. | ||
| if (process.env.NODE_ENV !== `production`) { | ||
| if ( | ||
| nextProps && | ||
| nextProps.pageResources && | ||
| nextProps.pageResources.json | ||
| ) { | ||
| this.setState({ pageResources: nextProps.pageResources }) | ||
| } | ||
| } | ||
| if (this.state.location.pathname !== nextProps.location.pathname) { | ||
| const pageResources = loader.getResourcesForPathname( | ||
| nextProps.location.pathname | ||
| ) | ||
| if (!pageResources) { | ||
| let location = nextProps.location | ||
|
|
||
| // Set the pathname for 404 pages. | ||
| if (!loader.getPage(location.pathname)) { | ||
| location = Object.assign({}, location, { | ||
| pathname: `/404.html`, | ||
| }) | ||
| } | ||
|
|
||
| // Page resources won't be set in cases where the browser back button | ||
| // or forward button is pushed as we can't wait as normal for resources | ||
| // to load before changing the page. | ||
| loader.getResourcesForPathname(location.pathname, pageResources => { | ||
| this.setState({ | ||
| location, | ||
| pageResources, | ||
| }) | ||
| }) | ||
| } else { | ||
| this.setState({ | ||
| location: nextProps.location, | ||
| pageResources, | ||
| }) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| componentDidMount() { | ||
| // Listen to events so when our page gets updated, we can transition. | ||
| // This is only useful on delayed transitions as the page will get rendered | ||
| // without the necessary page resources and then re-render once those come in. | ||
| emitter.on(`onPostLoadPageResources`, e => { | ||
| if ( | ||
| loader.getPage(this.state.location.pathname) && | ||
| e.page.path === loader.getPage(this.state.location.pathname).path | ||
| ) { | ||
| this.setState({ pageResources: e.pageResources }) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| shouldComponentUpdate(nextProps, nextState) { | ||
| // 404 | ||
| if (!nextState.pageResources) { | ||
| return true | ||
| } | ||
| // Check if the component or json have changed. | ||
| if (!this.state.pageResources && nextState.pageResources) { | ||
| return true | ||
| } | ||
| if ( | ||
| this.state.pageResources.component !== nextState.pageResources.component | ||
| ) { | ||
| return true | ||
| } | ||
|
|
||
| if (this.state.pageResources.json !== nextState.pageResources.json) { | ||
| return true | ||
| } | ||
|
|
||
| // Check if location has changed on a page using internal routing | ||
| // via matchPath configuration. | ||
| if ( | ||
| this.state.location.key !== nextState.location.key && | ||
| nextState.pageResources.page && | ||
| (nextState.pageResources.page.matchPath || | ||
| nextState.pageResources.page.path) | ||
| ) { | ||
| return true | ||
| } | ||
|
|
||
| return shallowCompare(this, nextProps, nextState) | ||
| } | ||
|
|
||
| render() { | ||
| const pluginResponses = apiRunner(`replaceComponentRenderer`, { | ||
| props: { ...this.props, pageResources: this.state.pageResources }, | ||
| loader: publicLoader, | ||
| }) | ||
| const replacementComponent = pluginResponses[0] | ||
| // If page. | ||
| if (this.props.page) { | ||
| if (this.state.pageResources) { | ||
| return ( | ||
| replacementComponent || | ||
| createElement(this.state.pageResources.component, { | ||
| key: this.props.location.pathname, | ||
| ...this.props, | ||
| ...this.state.pageResources.json, | ||
| }) | ||
| ) | ||
| } else { | ||
| return null | ||
| } | ||
| // If layout. | ||
| } else if (this.props.layout) { | ||
| return ( | ||
| replacementComponent || | ||
| createElement( | ||
| this.state.pageResources && this.state.pageResources.layout | ||
| ? this.state.pageResources.layout | ||
| : DefaultLayout, | ||
| { | ||
| key: | ||
| this.state.pageResources && this.state.pageResources.layout | ||
| ? this.state.pageResources.layout | ||
| : `DefaultLayout`, | ||
| ...this.props, | ||
| } | ||
| ) | ||
| ) | ||
| } else { | ||
| return null | ||
| } | ||
| } | ||
| } | ||
|
|
||
| ComponentRenderer.propTypes = { | ||
| page: PropTypes.bool, | ||
| layout: PropTypes.bool, | ||
| location: PropTypes.object, | ||
| } | ||
|
|
||
| export default ComponentRenderer |
| @@ -0,0 +1,47 @@ | ||
| import React from "react" | ||
|
|
||
| let stylesStr | ||
| if (process.env.NODE_ENV === `production`) { | ||
| try { | ||
| stylesStr = require(`!raw-loader!../public/styles.css`) | ||
| } catch (e) { | ||
| console.log(e) | ||
| } | ||
| } | ||
|
|
||
| module.exports = class HTML extends React.Component { | ||
| render() { | ||
| let css | ||
| if (process.env.NODE_ENV === `production`) { | ||
| css = ( | ||
| <style | ||
| id="gatsby-inlined-css" | ||
| dangerouslySetInnerHTML={{ __html: stylesStr }} | ||
| /> | ||
| ) | ||
| } | ||
| return ( | ||
| <html {...this.props.htmlAttributes}> | ||
| <head> | ||
| <meta charSet="utf-8" /> | ||
| <meta httpEquiv="x-ua-compatible" content="ie=edge" /> | ||
| <meta | ||
| name="viewport" | ||
| content="width=device-width, initial-scale=1, shrink-to-fit=no" | ||
| /> | ||
| {this.props.headComponents} | ||
| {css} | ||
| </head> | ||
| <body {...this.props.bodyAttributes}> | ||
| {this.props.preBodyComponents} | ||
| <div | ||
| key={`body`} | ||
| id="___gatsby" | ||
| dangerouslySetInnerHTML={{ __html: this.props.body }} | ||
| /> | ||
| {this.props.postBodyComponents} | ||
| </body> | ||
| </html> | ||
| ) | ||
| } | ||
| } |
| @@ -0,0 +1,70 @@ | ||
| /* global graphql: false */ | ||
| import React from "react" | ||
| import Link from "gatsby-link" | ||
|
|
||
| class Dev404Page extends React.Component { | ||
| static propTypes = { | ||
| data: () => {}, | ||
| location: () => {}, | ||
| } | ||
| render() { | ||
| const pathname = this.props.location.pathname | ||
| let newFilePath | ||
| if (pathname === `/`) { | ||
| newFilePath = `src/pages/index.js` | ||
| } else if (pathname.slice(-1) === `/`) { | ||
| newFilePath = `src/pages${pathname.slice(0, -1)}.js` | ||
| } else { | ||
| newFilePath = `src/pages${pathname}.js` | ||
| } | ||
| return ( | ||
| <div> | ||
| <h1>Gatsby.js development 404 page</h1> | ||
| <p> | ||
| {`There's not a page yet at `} | ||
| <code>{pathname}</code> | ||
| </p> | ||
| <p> | ||
| Create a React.js component in your site directory at | ||
| {` `} | ||
| <code>{newFilePath}</code> | ||
| {` `} | ||
| and this page will automatically refresh to show the new page | ||
| component you created. | ||
| </p> | ||
| {this.props.data.allSitePage && | ||
| this.props.data.allSitePage.totalCount > 1 && ( | ||
| <div> | ||
| <p> | ||
| If you were trying to reach another page, perhaps you can find | ||
| it below. | ||
| </p> | ||
| <h2>Pages ({this.props.data.allSitePage.totalCount})</h2> | ||
| <ul> | ||
| {this.props.data.allSitePage.edges.map(({ node }) => ( | ||
| <li key={node.path}> | ||
| <Link to={node.path}>{node.path}</Link> | ||
| </li> | ||
| ))} | ||
| </ul> | ||
| </div> | ||
| )} | ||
| </div> | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| export default Dev404Page | ||
|
|
||
| export const pageQuery = graphql` | ||
| query Dev404Page { | ||
| allSitePage(filter: { path: { ne: "/dev-404-page/" } }) { | ||
| totalCount | ||
| edges { | ||
| node { | ||
| path | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ` |
| @@ -0,0 +1,76 @@ | ||
| import React from "react" | ||
| import { renderToStaticMarkup } from "react-dom/server" | ||
| import { merge } from "lodash" | ||
| import testRequireError from "./test-require-error" | ||
| import apiRunner from "./api-runner-ssr" | ||
|
|
||
| let HTML | ||
| try { | ||
| HTML = require(`../src/html`) | ||
| } catch (err) { | ||
| if (testRequireError(`..\/src\/html`, err)) { | ||
| HTML = require(`./default-html`) | ||
| } else { | ||
| console.log(`There was an error requiring "src/html.js"\n\n`, err, `\n\n`) | ||
| process.exit() | ||
| } | ||
| } | ||
|
|
||
| module.exports = (locals, callback) => { | ||
| let headComponents = [] | ||
| let htmlAttributes = {} | ||
| let bodyAttributes = {} | ||
| let preBodyComponents = [] | ||
| let postBodyComponents = [] | ||
| let bodyProps = {} | ||
| let htmlStr | ||
|
|
||
| const setHeadComponents = components => { | ||
| headComponents = headComponents.concat(components) | ||
| } | ||
|
|
||
| const setHtmlAttributes = attributes => { | ||
| htmlAttributes = merge(htmlAttributes, attributes) | ||
| } | ||
|
|
||
| const setBodyAttributes = attributes => { | ||
| bodyAttributes = merge(bodyAttributes, attributes) | ||
| } | ||
|
|
||
| const setPreBodyComponents = components => { | ||
| preBodyComponents = preBodyComponents.concat(components) | ||
| } | ||
|
|
||
| const setPostBodyComponents = components => { | ||
| postBodyComponents = postBodyComponents.concat(components) | ||
| } | ||
|
|
||
| const setBodyProps = props => { | ||
| bodyProps = merge({}, bodyProps, props) | ||
| } | ||
|
|
||
| apiRunner(`onRenderBody`, { | ||
| setHeadComponents, | ||
| setHtmlAttributes, | ||
| setBodyAttributes, | ||
| setPreBodyComponents, | ||
| setPostBodyComponents, | ||
| setBodyProps, | ||
| }) | ||
|
|
||
| const htmlElement = React.createElement(HTML, { | ||
| ...bodyProps, | ||
| body: ``, | ||
| headComponents: headComponents.concat([ | ||
| <script key={`io`} src="/socket.io/socket.io.js" />, | ||
| ]), | ||
| preBodyComponents, | ||
| postBodyComponents: postBodyComponents.concat([ | ||
| <script key={`commons`} src="/commons.js" />, | ||
| ]), | ||
| }) | ||
| htmlStr = renderToStaticMarkup(htmlElement) | ||
| htmlStr = `<!DOCTYPE html>${htmlStr}` | ||
|
|
||
| callback(null, htmlStr) | ||
| } |
| @@ -0,0 +1,3 @@ | ||
| import mitt from "mitt" | ||
| const emitter = mitt() | ||
| module.exports = emitter |
| @@ -0,0 +1,77 @@ | ||
| // TODO add tests especially for handling prefixed links. | ||
| import { matchPath } from "react-router-dom" | ||
| import stripPrefix from "./strip-prefix" | ||
|
|
||
| const pageCache = {} | ||
|
|
||
| module.exports = (pages, pathPrefix = ``) => rawPathname => { | ||
| let pathname = decodeURIComponent(rawPathname) | ||
|
|
||
| // Remove the pathPrefix from the pathname. | ||
| let trimmedPathname = stripPrefix(pathname, pathPrefix) | ||
|
|
||
| // Remove any hashfragment | ||
| if (trimmedPathname.split(`#`).length > 1) { | ||
| trimmedPathname = trimmedPathname | ||
| .split(`#`) | ||
| .slice(0, -1) | ||
| .join(``) | ||
| } | ||
|
|
||
| // Remove search query | ||
| if (trimmedPathname.split(`?`).length > 1) { | ||
| trimmedPathname = trimmedPathname | ||
| .split(`?`) | ||
| .slice(0, -1) | ||
| .join(``) | ||
| } | ||
|
|
||
| if (pageCache[trimmedPathname]) { | ||
| return pageCache[trimmedPathname] | ||
| } | ||
|
|
||
| let foundPage | ||
| // Array.prototype.find is not supported in IE so we use this somewhat odd | ||
| // work around. | ||
| pages.some(page => { | ||
| if (page.matchPath) { | ||
| // Try both the path and matchPath | ||
| if ( | ||
| matchPath(trimmedPathname, { path: page.path }) || | ||
| matchPath(trimmedPathname, { | ||
| path: page.matchPath, | ||
| }) | ||
| ) { | ||
| foundPage = page | ||
| pageCache[trimmedPathname] = page | ||
| return true | ||
| } | ||
| } else { | ||
| if ( | ||
| matchPath(trimmedPathname, { | ||
| path: page.path, | ||
| exact: true, | ||
| }) | ||
| ) { | ||
| foundPage = page | ||
| pageCache[trimmedPathname] = page | ||
| return true | ||
| } | ||
|
|
||
| // Finally, try and match request with default document. | ||
| if ( | ||
| matchPath(trimmedPathname, { | ||
| path: page.path + `index.html`, | ||
| }) | ||
| ) { | ||
| foundPage = page | ||
| pageCache[trimmedPathname] = page | ||
| return true | ||
| } | ||
| } | ||
|
|
||
| return false | ||
| }) | ||
|
|
||
| return foundPage | ||
| } |
| @@ -0,0 +1,7 @@ | ||
| import createHistory from "history/createBrowserHistory" | ||
| import { apiRunner } from "./api-runner-browser" | ||
|
|
||
| const pluginResponses = apiRunner(`replaceHistory`) | ||
| const replacementHistory = pluginResponses[0] | ||
| const history = replacementHistory || createHistory() | ||
| module.exports = history |
| @@ -0,0 +1 @@ | ||
| {"data":{"allSitePage":{"totalCount":2,"edges":[{"node":{"path":"/"}},{"node":{"path":"/first-post"}}]}},"pathContext":{}} |
| @@ -0,0 +1 @@ | ||
| {"data":{"markdownRemark":{"html":"<p>Nunc blandit nisi ligula magna sodales lectus elementum non. Integer id venenatis velit.</p>","frontmatter":{"date":"March 21, 2000","path":"/first-post","title":"Magna feugiat lorem"}}},"pathContext":{}} |
| @@ -0,0 +1 @@ | ||
| {"pathContext":{}} |
| @@ -0,0 +1,20 @@ | ||
| [ | ||
| { | ||
| "componentChunkName": "component---themes-photon-pages-index-js", | ||
| "layout": null, | ||
| "jsonName": "index.json", | ||
| "path": "/" | ||
| }, | ||
| { | ||
| "componentChunkName": "component---themes-photon-templates-post-js", | ||
| "layout": null, | ||
| "jsonName": "first-post.json", | ||
| "path": "/first-post" | ||
| }, | ||
| { | ||
| "componentChunkName": "component---cache-dev-404-page-js", | ||
| "layout": null, | ||
| "jsonName": "dev-404-page.json", | ||
| "path": "/dev-404-page/" | ||
| } | ||
| ] |
| @@ -0,0 +1,70 @@ | ||
| module.exports = ({ getNextQueuedResources, createResourceDownload }) => { | ||
| let pagesLoading = [] | ||
| let resourcesDownloading = [] | ||
|
|
||
| // Do things | ||
| const startResourceDownloading = () => { | ||
| const nextResource = getNextQueuedResources() | ||
| if (nextResource) { | ||
| resourcesDownloading.push(nextResource) | ||
| createResourceDownload(nextResource) | ||
| } | ||
| } | ||
|
|
||
| const reducer = action => { | ||
| switch (action.type) { | ||
| case `RESOURCE_FINISHED`: | ||
| resourcesDownloading = resourcesDownloading.filter( | ||
| r => r !== action.payload | ||
| ) | ||
| break | ||
| case `ON_PRE_LOAD_PAGE_RESOURCES`: | ||
| pagesLoading.push(action.payload.path) | ||
| break | ||
| case `ON_POST_LOAD_PAGE_RESOURCES`: | ||
| pagesLoading = pagesLoading.filter(p => p !== action.payload.page.path) | ||
| break | ||
| case `ON_NEW_RESOURCES_ADDED`: | ||
| break | ||
| } | ||
|
|
||
| // Take actions. | ||
| // Wait for event loop queue to finish. | ||
| setTimeout(() => { | ||
| if (resourcesDownloading.length === 0 && pagesLoading.length === 0) { | ||
| // Start another resource downloading. | ||
| startResourceDownloading() | ||
| } | ||
| }, 0) | ||
| } | ||
|
|
||
| return { | ||
| onResourcedFinished: event => { | ||
| // Tell prefetcher that the resource finished downloading | ||
| // so it can grab the next one. | ||
| reducer({ type: `RESOURCE_FINISHED`, payload: event }) | ||
| }, | ||
| onPreLoadPageResources: event => { | ||
| // Tell prefetcher a page load has started so it should stop | ||
| // loading anything new | ||
| reducer({ type: `ON_PRE_LOAD_PAGE_RESOURCES`, payload: event }) | ||
| }, | ||
| onPostLoadPageResources: event => { | ||
| // Tell prefetcher a page load has finished so it should start | ||
| // loading resources again. | ||
| reducer({ type: `ON_POST_LOAD_PAGE_RESOURCES`, payload: event }) | ||
| }, | ||
| onNewResourcesAdded: () => { | ||
| // Tell prefetcher that more resources to be downloaded have | ||
| // been added. | ||
| reducer({ type: `ON_NEW_RESOURCES_ADDED` }) | ||
| }, | ||
| getState: () => { | ||
| return { pagesLoading, resourcesDownloading } | ||
| }, | ||
| empty: () => { | ||
| pagesLoading = [] | ||
| resourcesDownloading = [] | ||
| }, | ||
| } | ||
| } |
| @@ -0,0 +1,201 @@ | ||
| if (__POLYFILL__) { | ||
| require(`core-js/fn/promise`) | ||
| } | ||
| import { apiRunner, apiRunnerAsync } from "./api-runner-browser" | ||
| import React, { createElement } from "react" | ||
| import ReactDOM from "react-dom" | ||
| import { Router, Route, withRouter, matchPath } from "react-router-dom" | ||
| import { ScrollContext } from "gatsby-react-router-scroll" | ||
| import domReady from "domready" | ||
| import { createLocation } from "history" | ||
| import history from "./history" | ||
| window.___history = history | ||
| import emitter from "./emitter" | ||
| window.___emitter = emitter | ||
| import pages from "./pages.json" | ||
| import redirects from "./redirects.json" | ||
| import ComponentRenderer from "./component-renderer" | ||
| import asyncRequires from "./async-requires" | ||
| import loader from "./loader" | ||
| loader.addPagesArray(pages) | ||
| loader.addProdRequires(asyncRequires) | ||
| window.asyncRequires = asyncRequires | ||
| window.___loader = loader | ||
| window.matchPath = matchPath | ||
|
|
||
| // Convert to a map for faster lookup in maybeRedirect() | ||
| const redirectMap = redirects.reduce((map, redirect) => { | ||
| map[redirect.fromPath] = redirect | ||
| return map | ||
| }, {}) | ||
|
|
||
| const maybeRedirect = pathname => { | ||
| const redirect = redirectMap[pathname] | ||
|
|
||
| if (redirect != null) { | ||
| history.replace(redirect.toPath) | ||
| return true | ||
| } else { | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| // Check for initial page-load redirect | ||
| maybeRedirect(window.location.pathname) | ||
|
|
||
| // Let the site/plugins run code very early. | ||
| apiRunnerAsync(`onClientEntry`).then(() => { | ||
| // Let plugins register a service worker. The plugin just needs | ||
| // to return true. | ||
| if (apiRunner(`registerServiceWorker`).length > 0) { | ||
| require(`./register-service-worker`) | ||
| } | ||
|
|
||
| const navigateTo = to => { | ||
| const location = createLocation(to, null, null, history.location) | ||
| let { pathname } = location | ||
| const redirect = redirectMap[pathname] | ||
|
|
||
| // If we're redirecting, just replace the passed in pathname | ||
| // to the one we want to redirect to. | ||
| if (redirect) { | ||
| pathname = redirect.toPath | ||
| } | ||
| const wl = window.location | ||
|
|
||
| // If we're already at this location, do nothing. | ||
| if ( | ||
| wl.pathname === location.pathname && | ||
| wl.search === location.search && | ||
| wl.hash === location.hash | ||
| ) { | ||
| return | ||
| } | ||
|
|
||
| // Listen to loading events. If page resources load before | ||
| // a second, navigate immediately. | ||
| function eventHandler(e) { | ||
| if (e.page.path === loader.getPage(pathname).path) { | ||
| emitter.off(`onPostLoadPageResources`, eventHandler) | ||
| clearTimeout(timeoutId) | ||
| window.___history.push(location) | ||
| } | ||
| } | ||
|
|
||
| // Start a timer to wait for a second before transitioning and showing a | ||
| // loader in case resources aren't around yet. | ||
| const timeoutId = setTimeout(() => { | ||
| emitter.off(`onPostLoadPageResources`, eventHandler) | ||
| emitter.emit(`onDelayedLoadPageResources`, { pathname }) | ||
| window.___history.push(location) | ||
| }, 1000) | ||
|
|
||
| if (loader.getResourcesForPathname(pathname)) { | ||
| // The resources are already loaded so off we go. | ||
| clearTimeout(timeoutId) | ||
| window.___history.push(location) | ||
| } else { | ||
| // They're not loaded yet so let's add a listener for when | ||
| // they finish loading. | ||
| emitter.on(`onPostLoadPageResources`, eventHandler) | ||
| } | ||
| } | ||
|
|
||
| // window.___loadScriptsForPath = loadScriptsForPath | ||
| window.___navigateTo = navigateTo | ||
|
|
||
| // Call onRouteUpdate on the initial page load. | ||
| apiRunner(`onRouteUpdate`, { | ||
| location: history.location, | ||
| action: history.action, | ||
| }) | ||
|
|
||
| let initialAttachDone = false | ||
| function attachToHistory(history) { | ||
| if (!window.___history || initialAttachDone === false) { | ||
| window.___history = history | ||
| initialAttachDone = true | ||
|
|
||
| history.listen((location, action) => { | ||
| if (!maybeRedirect(location.pathname)) { | ||
| // Make sure React has had a chance to flush to DOM first. | ||
| setTimeout(() => { | ||
| apiRunner(`onRouteUpdate`, { location, action }) | ||
| }, 0) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| function shouldUpdateScroll(prevRouterProps, { location: { pathname } }) { | ||
| const results = apiRunner(`shouldUpdateScroll`, { | ||
| prevRouterProps, | ||
| pathname, | ||
| }) | ||
| if (results.length > 0) { | ||
| return results[0] | ||
| } | ||
|
|
||
| if (prevRouterProps) { | ||
| const { location: { pathname: oldPathname } } = prevRouterProps | ||
| if (oldPathname === pathname) { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } | ||
|
|
||
| const AltRouter = apiRunner(`replaceRouterComponent`, { history })[0] | ||
| const DefaultRouter = ({ children }) => ( | ||
| <Router history={history}>{children}</Router> | ||
| ) | ||
|
|
||
| const ComponentRendererWithRouter = withRouter(ComponentRenderer) | ||
|
|
||
| loader.getResourcesForPathname(window.location.pathname, () => { | ||
| const Root = () => | ||
| createElement( | ||
| AltRouter ? AltRouter : DefaultRouter, | ||
| null, | ||
| createElement( | ||
| ScrollContext, | ||
| { shouldUpdateScroll }, | ||
| createElement(ComponentRendererWithRouter, { | ||
| layout: true, | ||
| children: layoutProps => | ||
| createElement(Route, { | ||
| render: routeProps => { | ||
| attachToHistory(routeProps.history) | ||
| const props = layoutProps ? layoutProps : routeProps | ||
|
|
||
| if (loader.getPage(props.location.pathname)) { | ||
| return createElement(ComponentRenderer, { | ||
| page: true, | ||
| ...props, | ||
| }) | ||
| } else { | ||
| return createElement(ComponentRenderer, { | ||
| page: true, | ||
| location: { pathname: `/404.html` }, | ||
| }) | ||
| } | ||
| }, | ||
| }), | ||
| }) | ||
| ) | ||
| ) | ||
|
|
||
| const NewRoot = apiRunner(`wrapRootComponent`, { Root }, Root)[0] | ||
| domReady(() => | ||
| ReactDOM.render( | ||
| <NewRoot />, | ||
| typeof window !== `undefined` | ||
| ? document.getElementById(`___gatsby`) | ||
| : void 0, | ||
| () => { | ||
| apiRunner(`onInitialClientRender`) | ||
| } | ||
| ) | ||
| ) | ||
| }) | ||
| }) |
| @@ -0,0 +1 @@ | ||
| [] |
| @@ -0,0 +1,44 @@ | ||
| import emitter from "./emitter" | ||
|
|
||
| let pathPrefix = `/` | ||
| if (__PREFIX_PATHS__) { | ||
| pathPrefix = __PATH_PREFIX__ + `/` | ||
| } | ||
|
|
||
| if (`serviceWorker` in navigator) { | ||
| navigator.serviceWorker | ||
| .register(`${pathPrefix}sw.js`) | ||
| .then(function(reg) { | ||
| reg.addEventListener(`updatefound`, () => { | ||
| // The updatefound event implies that reg.installing is set; see | ||
| // https://w3c.github.io/ServiceWorker/#service-worker-registration-updatefound-event | ||
| const installingWorker = reg.installing | ||
| console.log(`installingWorker`, installingWorker) | ||
| installingWorker.addEventListener(`statechange`, () => { | ||
| switch (installingWorker.state) { | ||
| case `installed`: | ||
| if (navigator.serviceWorker.controller) { | ||
| // At this point, the old content will have been purged and the fresh content will | ||
| // have been added to the cache. | ||
| // We reload immediately so the user sees the new content. | ||
| // This could/should be made configurable in the future. | ||
| window.location.reload() | ||
| } else { | ||
| // At this point, everything has been precached. | ||
| // It's the perfect time to display a "Content is cached for offline use." message. | ||
| console.log(`Content is now available offline!`) | ||
| emitter.emit(`sw:installed`) | ||
| } | ||
| break | ||
|
|
||
| case `redundant`: | ||
| console.error(`The installing service worker became redundant.`) | ||
| break | ||
| } | ||
| }) | ||
| }) | ||
| }) | ||
| .catch(function(e) { | ||
| console.error(`Error during service worker registration:`, e) | ||
| }) | ||
| } |
| @@ -0,0 +1,201 @@ | ||
| import React, { createElement } from "react" | ||
| import { Router, Route, matchPath, withRouter } from "react-router-dom" | ||
| import { ScrollContext } from "gatsby-react-router-scroll" | ||
| import history from "./history" | ||
| import { apiRunner } from "./api-runner-browser" | ||
| import syncRequires from "./sync-requires" | ||
| import pages from "./pages.json" | ||
| import redirects from "./redirects.json" | ||
| import ComponentRenderer from "./component-renderer" | ||
| import loader from "./loader" | ||
|
|
||
| import * as ErrorOverlay from "react-error-overlay" | ||
|
|
||
| // Report runtime errors | ||
| ErrorOverlay.startReportingRuntimeErrors({ | ||
| onError: () => {}, | ||
| filename: `/commons.js`, | ||
| }) | ||
| ErrorOverlay.setEditorHandler(errorLocation => | ||
| window.fetch( | ||
| `/__open-stack-frame-in-editor?fileName=` + | ||
| window.encodeURIComponent(errorLocation.fileName) + | ||
| `&lineNumber=` + | ||
| window.encodeURIComponent(errorLocation.lineNumber || 1) | ||
| ) | ||
| ) | ||
|
|
||
| if (window.__webpack_hot_middleware_reporter__ !== undefined) { | ||
| // Report build errors | ||
| window.__webpack_hot_middleware_reporter__.useCustomOverlay({ | ||
| showProblems(type, obj) { | ||
| if (type !== `errors`) { | ||
| ErrorOverlay.dismissBuildError() | ||
| return | ||
| } | ||
| ErrorOverlay.reportBuildError(obj[0]) | ||
| }, | ||
| clear() { | ||
| ErrorOverlay.dismissBuildError() | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| loader.addPagesArray(pages) | ||
| loader.addDevRequires(syncRequires) | ||
| window.___loader = loader | ||
|
|
||
| // Convert to a map for faster lookup in maybeRedirect() | ||
| const redirectMap = redirects.reduce((map, redirect) => { | ||
| map[redirect.fromPath] = redirect | ||
| return map | ||
| }, {}) | ||
|
|
||
| // Check for initial page-load redirect | ||
| maybeRedirect(location.pathname) | ||
|
|
||
| // Call onRouteUpdate on the initial page load. | ||
| apiRunner(`onRouteUpdate`, { | ||
| location: history.location, | ||
| action: history.action, | ||
| }) | ||
|
|
||
| function attachToHistory(history) { | ||
| if (!window.___history) { | ||
| window.___history = history | ||
|
|
||
| history.listen((location, action) => { | ||
| if (!maybeRedirect(location.pathname)) { | ||
| apiRunner(`onRouteUpdate`, { location, action }) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| function maybeRedirect(pathname) { | ||
| const redirect = redirectMap[pathname] | ||
|
|
||
| if (redirect != null) { | ||
| const pageResources = loader.getResourcesForPathname(pathname) | ||
|
|
||
| if (pageResources != null) { | ||
| console.error( | ||
| `The route "${pathname}" matches both a page and a redirect; this is probably not intentional.` | ||
| ) | ||
| } | ||
|
|
||
| history.replace(redirect.toPath) | ||
| return true | ||
| } else { | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| function shouldUpdateScroll(prevRouterProps, { location: { pathname } }) { | ||
| const results = apiRunner(`shouldUpdateScroll`, { | ||
| prevRouterProps, | ||
| pathname, | ||
| }) | ||
| if (results.length > 0) { | ||
| return results[0] | ||
| } | ||
|
|
||
| if (prevRouterProps) { | ||
| const { location: { pathname: oldPathname } } = prevRouterProps | ||
| if (oldPathname === pathname) { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } | ||
|
|
||
| let noMatch | ||
| for (let i = 0; i < pages.length; i++) { | ||
| if (/^\/dev-404-page/.test(pages[i].path)) { | ||
| noMatch = pages[i] | ||
| break | ||
| } | ||
| } | ||
|
|
||
| const addNotFoundRoute = () => { | ||
| if (noMatch) { | ||
| return createElement(Route, { | ||
| key: `404-page`, | ||
| component: props => | ||
| createElement(syncRequires.components[noMatch.componentChunkName], { | ||
| ...props, | ||
| ...syncRequires.json[noMatch.jsonName], | ||
| }), | ||
| }) | ||
| } else { | ||
| return null | ||
| } | ||
| } | ||
|
|
||
| const navigateTo = to => { | ||
| window.___history.push(to) | ||
| } | ||
|
|
||
| window.___navigateTo = navigateTo | ||
|
|
||
| const AltRouter = apiRunner(`replaceRouterComponent`, { history })[0] | ||
| const DefaultRouter = ({ children }) => ( | ||
| <Router history={history}>{children}</Router> | ||
| ) | ||
|
|
||
| const ComponentRendererWithRouter = withRouter(ComponentRenderer) | ||
|
|
||
| // Always have to have one top-level layout | ||
| // can have ones below that. Find page, if has different | ||
| // parent layout(s), loop through those until finally the | ||
| // page. Tricky part is avoiding re-mounting I think... | ||
|
|
||
| const Root = () => | ||
| createElement( | ||
| AltRouter ? AltRouter : DefaultRouter, | ||
| null, | ||
| createElement( | ||
| ScrollContext, | ||
| { shouldUpdateScroll }, | ||
| createElement(ComponentRendererWithRouter, { | ||
| layout: true, | ||
| children: layoutProps => | ||
| createElement(Route, { | ||
| render: routeProps => { | ||
| const props = layoutProps ? layoutProps : routeProps | ||
| attachToHistory(props.history) | ||
| const { pathname } = props.location | ||
| const pageResources = loader.getResourcesForPathname(pathname) | ||
| if (pageResources && pageResources.component) { | ||
| return createElement(ComponentRenderer, { | ||
| key: `normal-page`, | ||
| page: true, | ||
| ...props, | ||
| pageResources, | ||
| }) | ||
| } else { | ||
| const dev404Page = pages.find(p => | ||
| /^\/dev-404-page/.test(p.path) | ||
| ) | ||
| return createElement(Route, { | ||
| key: `404-page`, | ||
| component: props => | ||
| createElement( | ||
| syncRequires.components[dev404Page.componentChunkName], | ||
| { | ||
| ...props, | ||
| ...syncRequires.json[dev404Page.jsonName], | ||
| } | ||
| ), | ||
| }) | ||
| } | ||
| }, | ||
| }), | ||
| }) | ||
| ) | ||
| ) | ||
|
|
||
| // Let site, plugins wrap the site e.g. for Redux. | ||
| const WrappedRoot = apiRunner(`wrapRootComponent`, { Root }, Root)[0] | ||
|
|
||
| export default WrappedRoot |
| @@ -0,0 +1,10 @@ | ||
| export default function shallowDiffers(a, b) { | ||
| console.log(`shallow differ`, a, b) | ||
| for (var i in a) { | ||
| if (!(i in b)) return true | ||
| } | ||
| for (var _i in b) { | ||
| if (a[_i] !== b[_i]) return true | ||
| } | ||
| return false | ||
| } |
| @@ -0,0 +1,11 @@ | ||
| export default function socketIo() { | ||
| try { | ||
| const socket = window.io() | ||
|
|
||
| socket.on(`reload`, () => { | ||
| window.location.reload() | ||
| }) | ||
| } catch (err) { | ||
| console.error(`Could not connect to socket.io on dev server.`) | ||
| } | ||
| } |
| @@ -0,0 +1,224 @@ | ||
| import React from "react" | ||
| import { renderToString, renderToStaticMarkup } from "react-dom/server" | ||
| import { StaticRouter, Route, withRouter } from "react-router-dom" | ||
| import { kebabCase, get, merge, isArray, isString } from "lodash" | ||
|
|
||
| import apiRunner from "./api-runner-ssr" | ||
| import pages from "./pages.json" | ||
| import syncRequires from "./sync-requires" | ||
| import testRequireError from "./test-require-error" | ||
|
|
||
| let Html | ||
| try { | ||
| Html = require(`../src/html`) | ||
| } catch (err) { | ||
| if (testRequireError(`..\/src\/html`, err)) { | ||
| Html = require(`./default-html`) | ||
| } else { | ||
| console.log( | ||
| `\n\nThere was an error requiring "src/html.js"\n\n`, | ||
| err, | ||
| `\n\n` | ||
| ) | ||
| process.exit() | ||
| } | ||
| } | ||
|
|
||
| const pathChunkName = path => { | ||
| const name = path === `/` ? `index` : kebabCase(path) | ||
| return `path---${name}` | ||
| } | ||
|
|
||
| const getPage = path => pages.find(page => page.path === path) | ||
| const defaultLayout = props => <div>{props.children()}</div> | ||
|
|
||
| const getLayout = page => { | ||
| const layout = syncRequires.layouts[page.layout] | ||
| return layout ? layout : defaultLayout | ||
| } | ||
|
|
||
| const createElement = React.createElement | ||
|
|
||
| module.exports = (locals, callback) => { | ||
| let pathPrefix = `/` | ||
| if (__PREFIX_PATHS__) { | ||
| pathPrefix = `${__PATH_PREFIX__}/` | ||
| } | ||
|
|
||
| let bodyHtml = `` | ||
| let headComponents = [] | ||
| let htmlAttributes = {} | ||
| let bodyAttributes = {} | ||
| let preBodyComponents = [] | ||
| let postBodyComponents = [] | ||
| let bodyProps = {} | ||
|
|
||
| const replaceBodyHTMLString = body => { | ||
| bodyHtml = body | ||
| } | ||
|
|
||
| const setHeadComponents = components => { | ||
| headComponents = headComponents.concat(components) | ||
| } | ||
|
|
||
| const setHtmlAttributes = attributes => { | ||
| htmlAttributes = merge(htmlAttributes, attributes) | ||
| } | ||
|
|
||
| const setBodyAttributes = attributes => { | ||
| bodyAttributes = merge(bodyAttributes, attributes) | ||
| } | ||
|
|
||
| const setPreBodyComponents = components => { | ||
| preBodyComponents = preBodyComponents.concat(components) | ||
| } | ||
|
|
||
| const setPostBodyComponents = components => { | ||
| postBodyComponents = postBodyComponents.concat(components) | ||
| } | ||
|
|
||
| const setBodyProps = props => { | ||
| bodyProps = merge({}, bodyProps, props) | ||
| } | ||
|
|
||
| const bodyComponent = createElement( | ||
| StaticRouter, | ||
| { | ||
| location: { | ||
| pathname: locals.path, | ||
| }, | ||
| context: {}, | ||
| }, | ||
| createElement(Route, { | ||
| render: routeProps => { | ||
| const page = getPage(routeProps.location.pathname) | ||
| const layout = getLayout(page) | ||
| return createElement(withRouter(layout), { | ||
| children: layoutProps => { | ||
| const props = layoutProps ? layoutProps : routeProps | ||
| return createElement( | ||
| syncRequires.components[page.componentChunkName], | ||
| { | ||
| ...props, | ||
| ...syncRequires.json[page.jsonName], | ||
| } | ||
| ) | ||
| }, | ||
| }) | ||
| }, | ||
| }) | ||
| ) | ||
|
|
||
| // Let the site or plugin render the page component. | ||
| apiRunner(`replaceRenderer`, { | ||
| bodyComponent, | ||
| replaceBodyHTMLString, | ||
| setHeadComponents, | ||
| setHtmlAttributes, | ||
| setBodyAttributes, | ||
| setPreBodyComponents, | ||
| setPostBodyComponents, | ||
| setBodyProps, | ||
| }) | ||
|
|
||
| // If no one stepped up, we'll handle it. | ||
| if (!bodyHtml) { | ||
| bodyHtml = renderToString(bodyComponent) | ||
| } | ||
|
|
||
| apiRunner(`onRenderBody`, { | ||
| setHeadComponents, | ||
| setHtmlAttributes, | ||
| setBodyAttributes, | ||
| setPreBodyComponents, | ||
| setPostBodyComponents, | ||
| setBodyProps, | ||
| pathname: locals.path, | ||
| bodyHtml, | ||
| }) | ||
|
|
||
| let stats | ||
| try { | ||
| stats = require(`../public/stats.json`) | ||
| } catch (e) { | ||
| // ignore | ||
| } | ||
|
|
||
| // Create paths to scripts | ||
| const page = pages.find(page => page.path === locals.path) | ||
| const scripts = [ | ||
| `commons`, | ||
| `app`, | ||
| pathChunkName(locals.path), | ||
| page.componentChunkName, | ||
| page.layoutComponentChunkName, | ||
| ] | ||
| .map(s => { | ||
| const fetchKey = `assetsByChunkName[${s}]` | ||
|
|
||
| let fetchedScript = get(stats, fetchKey) | ||
|
|
||
| if (!fetchedScript) { | ||
| return null | ||
| } | ||
|
|
||
| // If sourcemaps are enabled, then the entry will be an array with | ||
| // the script name as the first entry. | ||
| fetchedScript = isArray(fetchedScript) ? fetchedScript[0] : fetchedScript | ||
| const prefixedScript = `${pathPrefix}${fetchedScript}` | ||
|
|
||
| // Make sure we found a component. | ||
| if (prefixedScript === `/`) { | ||
| return null | ||
| } | ||
|
|
||
| return prefixedScript | ||
| }) | ||
| .filter(s => isString(s)) | ||
|
|
||
| scripts.forEach(script => { | ||
| // Add preload <link>s for scripts. | ||
| headComponents.unshift( | ||
| <link rel="preload" key={script} href={script} as="script" /> | ||
| ) | ||
| }) | ||
|
|
||
| // Add the chunk-manifest at the end of body element. | ||
| const chunkManifest = require(`!raw!../public/chunk-manifest.json`) | ||
| postBodyComponents.unshift( | ||
| <script | ||
| id="webpack-manifest" | ||
| key="webpack-manifest" | ||
| dangerouslySetInnerHTML={{ | ||
| __html: `/*<![CDATA[*/window.webpackManifest=${chunkManifest}/*]]>*/`, | ||
| }} | ||
| /> | ||
| ) | ||
|
|
||
| // Add script loader for page scripts to the end of body element (after webpack manifest). | ||
| // Taken from https://www.html5rocks.com/en/tutorials/speed/script-loading/ | ||
| const scriptsString = scripts.map(s => `"${s}"`).join(`,`) | ||
| postBodyComponents.push( | ||
| <script | ||
| key={`script-loader`} | ||
| dangerouslySetInnerHTML={{ | ||
| __html: `/*<![CDATA[*/!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[${scriptsString}])/*]]>*/`, | ||
| }} | ||
| /> | ||
| ) | ||
|
|
||
| const html = `<!DOCTYPE html>${renderToStaticMarkup( | ||
| <Html | ||
| {...bodyProps} | ||
| headComponents={headComponents} | ||
| htmlAttributes={htmlAttributes} | ||
| bodyAttributes={bodyAttributes} | ||
| preBodyComponents={preBodyComponents} | ||
| postBodyComponents={postBodyComponents} | ||
| body={bodyHtml} | ||
| path={locals.path} | ||
| /> | ||
| )}` | ||
|
|
||
| callback(null, html) | ||
| } |
| @@ -0,0 +1,9 @@ | ||
| /** | ||
| * Remove a prefix from a string. Return the input string if the given prefix | ||
| * isn't found. | ||
| */ | ||
|
|
||
| export default (str, prefix = ``) => { | ||
| if (str.substr(0, prefix.length) === prefix) return str.slice(prefix.length) | ||
| return str | ||
| } |
| @@ -0,0 +1,19 @@ | ||
| // prefer default export if available | ||
| const preferDefault = m => m && m.default || m | ||
|
|
||
|
|
||
| exports.layouts = { | ||
|
|
||
| } | ||
|
|
||
| exports.components = { | ||
| "component---themes-photon-pages-index-js": preferDefault(require("/Users/jonathanbry/LambdaSchool/TruckingWebsite/themes/photon/pages/index.js")), | ||
| "component---themes-photon-templates-post-js": preferDefault(require("/Users/jonathanbry/LambdaSchool/TruckingWebsite/themes/photon/templates/post.js")), | ||
| "component---cache-dev-404-page-js": preferDefault(require("/Users/jonathanbry/LambdaSchool/TruckingWebsite/.cache/dev-404-page.js")) | ||
| } | ||
|
|
||
| exports.json = { | ||
| "index.json": require("/Users/jonathanbry/LambdaSchool/TruckingWebsite/.cache/json/index.json"), | ||
| "first-post.json": require("/Users/jonathanbry/LambdaSchool/TruckingWebsite/.cache/json/first-post.json"), | ||
| "dev-404-page.json": require("/Users/jonathanbry/LambdaSchool/TruckingWebsite/.cache/json/dev-404-page.json") | ||
| } |
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "people": | ||
| [ | ||
| { | ||
| "name": "Luke Skywalker", | ||
| "email": "Luke@skywalker.com", | ||
| "created": "2014-12-09T13:50:51.644Z", | ||
| "id": 1 | ||
| }, | ||
| { | ||
| "name": "Jon Bry", | ||
| "email": "jonathanbry@comcast.net", | ||
| "created": "2014-12-09T13:50:51.644Z", | ||
| "id": 2 | ||
| } | ||
| ] | ||
| } |