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

[v2] Allow for plugins to override core prefetching behavior #5320

Merged
merged 8 commits into from
May 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/gatsby-link/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class GatsbyLink extends React.Component {
}

render() {
const { onClick, ...rest } = this.props
const { onClick, onMouseEnter, ...rest } = this.props
let El
if (Object.keys(NavLinkPropTypes).some(propName => this.props[propName])) {
El = NavLink
Expand All @@ -110,6 +110,11 @@ class GatsbyLink extends React.Component {

return (
<El
onMouseEnter={e => {
// eslint-disable-line
onMouseEnter && onMouseEnter(e)
___loader.hovering(this.state.path)
}}
onClick={e => {
// eslint-disable-line
onClick && onClick(e)
Expand Down
27 changes: 24 additions & 3 deletions packages/gatsby/cache-dir/loader.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pageFinderFactory from "./find-page"
import emitter from "./emitter"
import stripPrefix from "./strip-prefix"
import { apiRunner } from "./api-runner-browser"

const preferDefault = m => (m && m.default) || m

Expand Down Expand Up @@ -128,8 +129,6 @@ const appearsOnLine = () => {
}

const handleResourceLoadError = (path, message) => {
console.log(message)

if (!failedPaths[path]) {
failedPaths[path] = message
}
Expand Down Expand Up @@ -159,6 +158,9 @@ let findPage
let pathScriptsCache = {}
let resourcesArray = []
let mountOrder = 1
let prefetchTriggered = {}

const disableCorePrefetching = apiRunner(`disableCorePrefetching`)

const queue = {
empty: () => {
Expand All @@ -183,10 +185,29 @@ const queue = {
jsonDataPaths = dataPaths
},
dequeue: () => resourcesArray.pop(),
// Hovering on a link is a very strong indication the user is going to
// click on it soon so let's start prefetching resources for this
// pathname.
hovering: rawPath => {
const path = stripPrefix(rawPath, pathPrefix.slice(0, -1))
queue.getResourcesForPathname(path)
},
enqueue: rawPath => {
// Check page exists.
const path = stripPrefix(rawPath, pathPrefix.slice(0, -1))

// Tell plugins with custom prefetching logic that they should start
// prefetching this path.
if (!prefetchTriggered[path]) {
apiRunner(`onPrefetchPathname`, { pathname: path })
prefetchTriggered[path] = true
}

// If a plugin has disabled core prefetching, stop now.
if (disableCorePrefetching.some(a => a)) {
return false
}

// Check if the page exists.
let page = findPage(path)

if (
Expand Down
28 changes: 16 additions & 12 deletions packages/gatsby/cache-dir/static-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ export default (pagePath, callback) => {
},
createElement(Route, {
// eslint-disable-next-line react/display-name
render: routeProps => createElement(syncRequires.components[page.componentChunkName], {
render: routeProps =>
createElement(syncRequires.components[page.componentChunkName], {
...routeProps,
...dataAndContext,
}),
Expand All @@ -150,17 +151,6 @@ export default (pagePath, callback) => {
bodyHtml = renderToString(bodyComponent)
}

apiRunner(`onRenderBody`, {
setHeadComponents,
setHtmlAttributes,
setBodyAttributes,
setPreBodyComponents,
setPostBodyComponents,
setBodyProps,
pathname: pagePath,
bodyHtml,
})

// Create paths to scripts
let runtimeScript
const scriptsAndStyles = flatten(
Expand Down Expand Up @@ -193,6 +183,20 @@ export default (pagePath, callback) => {
const scripts = scriptsAndStyles.filter(s => s.endsWith(`.js`))
const styles = scriptsAndStyles.filter(s => s.endsWith(`.css`))

apiRunner(`onRenderBody`, {
setHeadComponents,
setHtmlAttributes,
setBodyAttributes,
setPreBodyComponents,
setPostBodyComponents,
setBodyProps,
pathname: pagePath,
bodyHtml,
scripts,
styles,
pathPrefix,
})

const runtimeRaw = fs.readFileSync(
join(process.cwd(), `public`, runtimeScript),
`utf-8`
Expand Down
24 changes: 14 additions & 10 deletions packages/gatsby/src/commands/build-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,20 @@ module.exports = async (program: any) => {
)
}

return renderHTML(require(outputFile), pages).then(() => {
// Remove the temp JS bundle file built for the static-site-generator-plugin
try {
fs.unlinkSync(outputFile)
fs.unlinkSync(`${outputFile}.map`)
} catch (e) {
// This function will fail on Windows with no further consequences.
}
return resolve(null, stats)
})
return renderHTML(require(outputFile), pages)
.then(() => {
// Remove the temp JS bundle file built for the static-site-generator-plugin
try {
fs.unlinkSync(outputFile)
fs.unlinkSync(`${outputFile}.map`)
} catch (e) {
// This function will fail on Windows with no further consequences.
}
return resolve(null, stats)
})
.catch(e => {
console.log(e)
})
})
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ const addDefaultPluginsPresets = (
actions,
{ stage = ``, browserslist = {} }
) => {
let targets
if (stage === `build-html`) {
targets = { node: `current` }
} else {
targets = { browsers: browserslist }
}
// Presets
actions.setBabelPreset({
name: `@babel/preset-env`,
Expand All @@ -155,7 +161,7 @@ const addDefaultPluginsPresets = (
useBuiltIns: `usage`,
sourceType: `unambiguous`,
shippedProposals: true, // includes async/await and Object spread/rest
targets: { browsers: browserslist },
targets,
},
})
actions.setBabelPreset({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const writePages = async () => {
const pagesComponentDependencies = {}

// Write out pages.json
const pagesData = pages.reduce(
(mem, { path, matchPath, componentChunkName, jsonName }) => {
const pagesData = _.sortBy(
pages.reduce((mem, { path, matchPath, componentChunkName, jsonName }) => {
const pageComponentsChunkNames = {
componentChunkName,
}
Expand All @@ -35,8 +35,10 @@ const writePages = async () => {
matchPath,
},
]
},
[]
}, []),
// Sort pages with matchPath to end so explicit routes
// will match before general.
p => (p.matchPath ? 1 : 0)
)

const newHash = crypto
Expand Down
14 changes: 14 additions & 0 deletions packages/gatsby/src/utils/api-browser-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,17 @@ exports.replaceHistory = true
* @param {object} $0.Root The "Root" component built by Gatsby.
*/
exports.wrapRootComponent = true

/**
* Called when prefetching for a pathname is triggered. Allows
* for plugins with custom prefetching logic.
* @param {object} $0
* @param {object} $0.pathname The pathname whose resources should now be prefetched
*/
exports.onPrefetchPathname = true

/**
* Plugins can take over prefetching logic. If they do, they should call this
* to disable the now duplicate core prefetching logic.
*/
exports.disableCorePrefetching = true
14 changes: 9 additions & 5 deletions packages/gatsby/src/utils/html-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ const generatePathToOutput = outputPath => {
}

module.exports = (htmlComponentRenderer, pages) =>
new Promise(resolve => {
new Promise((resolve, reject) => {
const queue = new Queue(
(path, callback) => {
htmlComponentRenderer.default(path, (throwAway, htmlString) => {
fs.outputFile(generatePathToOutput(path), htmlString).then(() => {
callback()
try {
htmlComponentRenderer.default(path, (throwAway, htmlString) => {
fs.outputFile(generatePathToOutput(path), htmlString).then(() => {
callback()
})
})
})
} catch (e) {
reject(e)
}
},
{
concurrent: 20,
Expand Down