Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new onPreRenderHTML SSR API. #6760

Merged
merged 13 commits into from
Aug 16, 2018
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`develop-static-entry onPreRenderHTML can be used to replace headComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><style>.style3 </style><style>.style2 </style><style>.style1 </style><script src=\\"/socket.io/socket.io.js\\"></script></head><body><div id=\\"___gatsby\\"></div><script src=\\"/commons.js\\"></script></body></html>"`;

exports[`develop-static-entry onPreRenderHTML can be used to replace postBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><script src=\\"/socket.io/socket.io.js\\"></script></head><body><div id=\\"___gatsby\\"></div><div>div3</div><div>div2</div><div>div1</div><script src=\\"/commons.js\\"></script></body></html>"`;

exports[`develop-static-entry onPreRenderHTML can be used to replace preBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><script src=\\"/socket.io/socket.io.js\\"></script></head><body><div>div3</div><div>div2</div><div>div1</div><div id=\\"___gatsby\\"></div><script src=\\"/commons.js\\"></script></body></html>"`;

exports[`static-entry onPreRenderHTML can be used to replace headComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><style>.style3 </style><style>.style2 </style><style>.style1 </style></head><body><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\"></div></div><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.page={\\"path\\":\\"/about/\\",\\"componentChunkName\\":\\"page-component---src-pages-test-js\\",\\"jsonName\\":\\"about.json\\"};/*]]>*/</script></body></html>"`;

exports[`static-entry onPreRenderHTML can be used to replace postBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/></head><body><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\"></div></div><div>div3</div><div>div2</div><div>div1</div><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.page={\\"path\\":\\"/about/\\",\\"componentChunkName\\":\\"page-component---src-pages-test-js\\",\\"jsonName\\":\\"about.json\\"};/*]]>*/</script></body></html>"`;

exports[`static-entry onPreRenderHTML can be used to replace preBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/></head><body><div>div3</div><div>div2</div><div>div1</div><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\"></div></div><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.page={\\"path\\":\\"/about/\\",\\"componentChunkName\\":\\"page-component---src-pages-test-js\\",\\"jsonName\\":\\"about.json\\"};/*]]>*/</script></body></html>"`;
167 changes: 167 additions & 0 deletions packages/gatsby/cache-dir/__tests__/static-entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import React from 'react'
import DevelopStaticEntry from '../develop-static-entry'

jest.mock(`fs`)

jest.mock(
`../sync-requires`,
() => {
return {
components: {
'page-component---src-pages-test-js': () => null,
},
}
},
{ virtual: true }
)

jest.mock(
`../data.json`,
() => {
return {
dataPaths: [{
[`about.json`]: `/400/about`,
}],
pages: [{
path: `/about/`,
componentChunkName: `page-component---src-pages-test-js`,
jsonName: `about.json`,
}],
}
},
{ virtual: true }
)


const MOCK_FILE_INFO = {
[`${process.cwd()}/public/webpack.stats.json`]: `{}`,
}

require(`fs`).__setMockFiles(MOCK_FILE_INFO)

// Needs to be imported after __setMockFiles is called, and imports get hoisted.
const StaticEntry = require(`../static-entry`).default

const reverseHeadersPlugin = {
plugin: {
onPreRenderHTML: ({ getHeadComponents, replaceHeadComponents }) => {
const headComponents = getHeadComponents()
headComponents.reverse()
replaceHeadComponents(headComponents)
},
},
}

const fakeStylesPlugin = {
plugin: {
onRenderBody: ({ setHeadComponents }) => setHeadComponents([
<style key="style1">.style1 {}</style>,
<style key="style2">.style2 {}</style>,
<style key="style3">.style3 {}</style>,
]),
},
}

const reverseBodyComponentsPluginFactory = (type) => {
return {
plugin: {
onPreRenderHTML: (props) => {
const components = props[`get${type}BodyComponents`]()
components.reverse()
props[`replace${type}BodyComponents`](components)
},
},
}}

const fakeComponentsPluginFactory = type => {
return {
plugin: {
onRenderBody: (props) => {
props[`set${type}BodyComponents`]([
<div key="div1">div1</div>,
<div key="div2">div2</div>,
<div key="div3">div3</div>,
])
},
},
}}

describe(`develop-static-entry`, () => {
test(`onPreRenderHTML can be used to replace headComponents`, (done) => {
global.plugins = [
fakeStylesPlugin,
reverseHeadersPlugin,
]

DevelopStaticEntry(`/about/`, (_, html) => {
expect(html).toMatchSnapshot()
done()
})
})

test(`onPreRenderHTML can be used to replace postBodyComponents`, (done) => {
global.plugins = [
fakeComponentsPluginFactory(`Post`),
reverseBodyComponentsPluginFactory(`Post`),
]

DevelopStaticEntry(`/about/`, (_, html) => {
expect(html).toMatchSnapshot()
done()
})
})

test(`onPreRenderHTML can be used to replace preBodyComponents`, (done) => {
global.plugins = [
fakeComponentsPluginFactory(`Pre`),
reverseBodyComponentsPluginFactory(`Pre`),
]

DevelopStaticEntry(`/about/`, (_, html) => {
expect(html).toMatchSnapshot()
done()
})
})
})

describe(`static-entry`, () => {
beforeEach(() => {
global.__PATH_PREFIX__ = ``
})

test(`onPreRenderHTML can be used to replace headComponents`, (done) => {
global.plugins = [
fakeStylesPlugin,
reverseHeadersPlugin,
]

StaticEntry(`/about/`, (_, html) => {
expect(html).toMatchSnapshot()
done()
})
})

test(`onPreRenderHTML can be used to replace postBodyComponents`, (done) => {
global.plugins = [
fakeComponentsPluginFactory(`Post`),
reverseBodyComponentsPluginFactory(`Post`),
]

StaticEntry(`/about/`, (_, html) => {
expect(html).toMatchSnapshot()
done()
})
})

test(`onPreRenderHTML can be used to replace preBodyComponents`, (done) => {
global.plugins = [
fakeComponentsPluginFactory(`Pre`),
reverseBodyComponentsPluginFactory(`Pre`),
]

StaticEntry(`/about/`, (_, html) => {
expect(html).toMatchSnapshot()
done()
})
})
})
38 changes: 38 additions & 0 deletions packages/gatsby/cache-dir/api-ssr-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,41 @@ exports.replaceStaticRouterComponent = true
* }
*/
exports.onRenderBody = true

/**
* Called after every page Gatsby server renders while building HTML so you can
* replace head components to be rendered in your `html.js`. This is useful if
* you need to reorder scripts or styles added by other plugins.
* @param {Object} $0
* @param {Array} $0.getHeadComponents Returns the current `headComponents` array.
* @param {function} $0.replaceHeadComponents Takes an array of components as its
* first argument which replace the `headComponents` array which is passed
* to the `html.js` component. **WARNING** if multiple plugins implement this
* API it's the last plugin that "wins".
* @param {Array} $0.getPreBodyComponents Returns the current `preBodyComponents` array.
* @param {function} $0.replacePreBodyComponents Takes an array of components as its
* first argument which replace the `preBodyComponents` array which is passed
* to the `html.js` component. **WARNING** if multiple plugins implement this
* API it's the last plugin that "wins".
* @param {Array} $0.getPostBodyComponents Returns the current `postBodyComponents` array.
* @param {function} $0.replacePostBodyComponents Takes an array of components as its
* first argument which replace the `postBodyComponents` array which is passed
* to the `html.js` component. **WARNING** if multiple plugins implement this
* API it's the last plugin that "wins".
* @param {Object} pluginOptions
* @example
* // Move Typography.js styles to the top of the head section so they're loaded first.
* exports.onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
* const headComponents = getHeadComponents()
* headComponents.sort((x, y) => {
* if (x.key === 'TypographyStyle') {
* return -1
* } else if (y.key === 'TypographyStyle') {
* return 1
* }
* return 0
* })
* replaceHeadComponents(headComponents)
* }
*/
exports.onPreRenderHTML = true
27 changes: 27 additions & 0 deletions packages/gatsby/cache-dir/develop-static-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,24 @@ export default (pagePath, callback) => {
bodyProps = merge({}, bodyProps, props)
}

const getHeadComponents = () => headComponents

const replaceHeadComponents = components => {
headComponents = components
}

const getPreBodyComponents = () => preBodyComponents

const replacePreBodyComponents = components => {
preBodyComponents = components
}

const getPostBodyComponents = () => postBodyComponents

const replacePostBodyComponents = components => {
postBodyComponents = components
}

apiRunner(`onRenderBody`, {
setHeadComponents,
setHtmlAttributes,
Expand All @@ -68,6 +86,15 @@ export default (pagePath, callback) => {
setBodyProps,
})

apiRunner(`onPreRenderHTML`, {
getHeadComponents,
replaceHeadComponents,
getPreBodyComponents,
replacePreBodyComponents,
getPostBodyComponents,
replacePostBodyComponents,
})

const htmlElement = React.createElement(Html, {
...bodyProps,
body: ``,
Expand Down
27 changes: 27 additions & 0 deletions packages/gatsby/cache-dir/static-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,24 @@ export default (pagePath, callback) => {
bodyProps = merge({}, bodyProps, props)
}

const getHeadComponents = () => headComponents

const replaceHeadComponents = components => {
headComponents = components
}

const getPreBodyComponents = () => preBodyComponents

const replacePreBodyComponents = components => {
preBodyComponents = components
}

const getPostBodyComponents = () => postBodyComponents

const replacePostBodyComponents = components => {
postBodyComponents = components
}

const page = getPage(pagePath)

let dataAndContext = {}
Expand Down Expand Up @@ -267,6 +285,15 @@ export default (pagePath, callback) => {
}
})

apiRunner(`onPreRenderHTML`, {
getHeadComponents,
replaceHeadComponents,
getPreBodyComponents,
replacePreBodyComponents,
getPostBodyComponents,
replacePostBodyComponents,
})

// Add page metadata for the current page
const windowData = `/*<![CDATA[*/window.page=${JSON.stringify(page)};${
page.jsonName in dataPaths
Expand Down