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

feat(gatsby): Move page component state & side effect handling to xstate #11897

Merged
merged 53 commits into from
Mar 26, 2019
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
d61316e
Inital work to move component query handling to xstate + fix bug with…
KyleAMathews Feb 19, 2019
5db0b2c
Expand model to include we don't want to immediately extract queries …
KyleAMathews Feb 19, 2019
2abcb2c
Rearrange states so more in order of possible events
KyleAMathews Feb 20, 2019
799391f
Merge remote-tracking branch 'origin/master' into xstate-components
KyleAMathews Feb 20, 2019
bbbd20b
Document two program statuses
KyleAMathews Feb 20, 2019
5f53d33
Move responsibility for running queries for pages to the page compone…
KyleAMathews Feb 20, 2019
d8fb163
Handle page components changing
KyleAMathews Feb 20, 2019
97226e6
Let state machine know when a query has been run and only re-run quer…
KyleAMathews Feb 21, 2019
9347c2f
Handle babel error state when extracing queries
KyleAMathews Feb 22, 2019
d242435
Make name more explicit
KyleAMathews Feb 22, 2019
a124367
Ignore static query results
KyleAMathews Feb 23, 2019
aaf8bf3
Clarify comment
KyleAMathews Feb 25, 2019
53eea1c
empty strings are falsy 🤦‍♂️
KyleAMathews Feb 25, 2019
0e4d4df
Handled in last commit
KyleAMathews Feb 25, 2019
df226de
Only handle clean ids for static queries (for now until model static …
KyleAMathews Feb 25, 2019
6f60428
In hacky/temp way (until modeling pages directly), check if a new pag…
KyleAMathews Feb 25, 2019
8631558
Mark new actions as private
KyleAMathews Feb 25, 2019
e5525b4
Move action to bootstrap per @stefanprobst's suggestion
KyleAMathews Feb 25, 2019
647b2dd
Extract page component machine into new 'machines' directory
KyleAMathews Feb 25, 2019
c3bb339
Fix check that there's a query in a non-page component
KyleAMathews Feb 26, 2019
3e0ddcf
Add xstate as a dependency
KyleAMathews Feb 26, 2019
9343d8e
Update snapshots
KyleAMathews Feb 26, 2019
0e4c346
Add initial tests for page component machine
KyleAMathews Feb 26, 2019
fd4edaa
Listen for query queue to empty to know queries are done
KyleAMathews Feb 26, 2019
50e9b0f
Comment out machine logging
KyleAMathews Feb 26, 2019
0066cad
Merge remote-tracking branch 'origin/master' into xstate-components
KyleAMathews Feb 26, 2019
10298a4
Restore usage of getCodeFromRelayError
KyleAMathews Feb 26, 2019
b98ea2a
Don't need to re-set service on Map
KyleAMathews Feb 27, 2019
836ef5e
Small fixes
KyleAMathews Feb 28, 2019
3b2006f
Update packages/gatsby/src/redux/reducers/components.js
wardpeet Feb 28, 2019
4f994f6
Don't run queries until hit query running stage in bootstrap
KyleAMathews Feb 28, 2019
2fb8794
Switch page components out of bootstrap state right after bootstrap q…
KyleAMathews Feb 28, 2019
1d3074f
Merge remote-tracking branch 'origin/master' into xstate-components
KyleAMathews Feb 28, 2019
cb23533
Merge remote-tracking branch 'origin/master' into xstate-components
KyleAMathews Mar 5, 2019
beebbea
During bootstrap, only run page queries that are 'clean' (i.e. we hav…
KyleAMathews Mar 5, 2019
17d34d9
Handle deleting pages in machine so it knows to re-run its query if r…
KyleAMathews Mar 5, 2019
37baf68
Move simple set context actions to top-level so don't have to repeat …
KyleAMathews Mar 7, 2019
5100484
Simplify logic for running queries for new pages
KyleAMathews Mar 8, 2019
a8c32e1
run queries for pages created after bootstrap
pieh Mar 11, 2019
9daeae2
Capture more types of graphql extraction errors
KyleAMathews Mar 13, 2019
c299ae6
Merge remote-tracking branch 'upstream/master' into xstate-components
DSchau Mar 13, 2019
36bda2e
DELETE_PAGE action use `component` not `componentPath`
pieh Mar 15, 2019
1adc63d
Page components shouldn't control extraction — moving that to them me…
KyleAMathews Mar 16, 2019
7932e8e
Now that we're not tracking query extracting, add event for when babe…
KyleAMathews Mar 16, 2019
f4bb7c2
Merge remote-tracking branch 'origin/master' into xstate-components
KyleAMathews Mar 16, 2019
20fcf49
Merge remote-tracking branch 'origin/master' into xstate-components
KyleAMathews Mar 22, 2019
03f4b37
Move graphql/babel event handling to top-level as we should always re…
KyleAMathews Mar 22, 2019
8dfe393
Move action call to after findGraphQLTags as we cache babel extraction
KyleAMathews Mar 22, 2019
cedc761
trivial change
KyleAMathews Mar 22, 2019
b3fd97b
Merge remote-tracking branch 'origin/master' into xstate-components
KyleAMathews Mar 25, 2019
83eedb2
yarn.lock change
KyleAMathews Mar 25, 2019
76475f5
Trivial change so can publish
KyleAMathews Mar 25, 2019
67ef857
Update from master
KyleAMathews Mar 26, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions packages/gatsby/cache-dir/static-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ export default (pagePath, callback) => {

let dataAndContext = {}
if (page.jsonName in dataPaths) {
const pathToJsonData = `../public/` + dataPaths[page.jsonName]
const pathToJsonData = join(
process.cwd(),
`/public/static/d`,
`${dataPaths[page.jsonName]}.json`
)
try {
dataAndContext = JSON.parse(
fs.readFileSync(
`${process.cwd()}/public/static/d/${dataPaths[page.jsonName]}.json`
)
)
dataAndContext = JSON.parse(fs.readFileSync(pathToJsonData))
} catch (e) {
console.log(`error`, pathToJsonData, e)
process.exit()
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
"webpack-hot-middleware": "^2.21.0",
"webpack-merge": "^4.1.0",
"webpack-stats-plugin": "^0.1.5",
"xstate": "^4.3.2",
"yaml-loader": "^0.5.0"
},
"devDependencies": {
Expand Down
13 changes: 12 additions & 1 deletion packages/gatsby/src/bootstrap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,9 +461,17 @@ module.exports = async (args: BootstrapArgs) => {
).toFixed(2)} queries/second`
)
})
await runInitialQueries(activity)
// HACKY!!! TODO: REMOVE IN NEXT REFACTOR
emitter.emit(`START_QUERY_QUEUE`)
// END HACKY
runInitialQueries(activity)
await new Promise(resolve => queryQueue.on(`drain`, resolve))
activity.end()

require(`../redux/actions`).boundActionCreators.setProgramStatus(
`BOOTSTRAP_QUERY_RUNNING_FINISHED`
)

// Write out files.
activity = report.activityTimer(`write out page data`, {
parentSpan: bootstrapSpan,
Expand Down Expand Up @@ -525,6 +533,9 @@ module.exports = async (args: BootstrapArgs) => {
report.info(`bootstrap finished - ${process.uptime()} s`)
report.log(``)
emitter.emit(`BOOTSTRAP_FINISHED`)
require(`../redux/actions`).boundActionCreators.setProgramStatus(
`BOOTSTRAP_FINISHED`
)
return {
graphqlRunner,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ Array [
"browserAPIs": Array [],
"id": "84dad27f-1d44-51fc-ac56-4db2e5222995",
"name": "query-runner",
"nodeAPIs": Array [
"onCreatePage",
],
"nodeAPIs": Array [],
"pluginOptions": Object {
"plugins": Array [],
},
Expand Down Expand Up @@ -181,9 +179,7 @@ Array [
"browserAPIs": Array [],
"id": "84dad27f-1d44-51fc-ac56-4db2e5222995",
"name": "query-runner",
"nodeAPIs": Array [
"onCreatePage",
],
"nodeAPIs": Array [],
"pluginOptions": Object {
"plugins": Array [],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { DocumentNode, DefinitionNode } from "graphql"
import { babelParseToAst } from "../../utils/babel-parse-to-ast"

const apiRunnerNode = require(`../../utils/api-runner-node`)
const { boundActionCreators } = require(`../../redux/actions`)

/**
* Add autogenerated query name if it wasn't defined by user.
Expand Down Expand Up @@ -63,6 +64,10 @@ async function parseToAst(filePath, fileStr) {
try {
ast = babelParseToAst(fileStr, filePath)
} catch (error) {
boundActionCreators.queryExtractionBabelError({
componentPath: filePath,
error,
})
report.error(
`There was a problem parsing "${filePath}"; any GraphQL ` +
`fragments or queries in this file were not processed. \n` +
Expand Down
20 changes: 0 additions & 20 deletions packages/gatsby/src/internal-plugins/query-runner/gatsby-node.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,22 @@ function formatError(message: string, filePath: string, codeFrame: string) {
}

function extractError(error: Error): { message: string, docName: string } {
const docRegex = /Invariant Violation: (RelayParser|GraphQLParser): (.*). Source: document `(.*)` file:/g
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regex broke when we moved to Relay Compiler v2.

const docRegex = /Error:.(RelayParser|GraphQLParser):(.*)Source: document.`(.*)`.file.*(GraphQL.request.*^\s*$)/gms
let matches
let message = ``,
docName = ``
let message = ``
let docName = ``
let codeBlock = ``
while ((matches = docRegex.exec(error.toString())) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (matches.index === docRegex.lastIndex) docRegex.lastIndex++
;[, , message, docName] = matches
;[, , message, docName, codeBlock] = matches
}

if (!message) {
message = error.toString()
}

return { message, docName }
return { message, codeBlock, docName }
}

function findLocation(extractedMessage, def) {
Expand Down Expand Up @@ -169,15 +170,18 @@ export function graphqlError(
nameDefMap: Map<string, any>,
error: Error | RelayGraphQLError
) {
let codeBlock
let { message, docName } = extractError(error)
let filePath = namePathMap.get(docName)

if (filePath && docName) {
return formatError(
codeBlock = getCodeFrameFromRelayError(
nameDefMap.get(docName),
message,
filePath,
getCodeFrameFromRelayError(nameDefMap.get(docName), message, error)
error
)
const formattedMessage = formatError(message, filePath, codeBlock)
return { formattedMessage, docName, message, codeBlock }
}

let reportedMessage = `There was an error while compiling your site's GraphQL queries.
Expand All @@ -194,5 +198,5 @@ export function graphqlError(
reportedMessage += `${error.message.slice(21)}\n`
}

return reportedMessage
return { formattedMessage: reportedMessage, docName, message, codeBlock }
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,17 @@ exports.queueQueryForPathname = pathname => {
// Afterwards we listen "API_RUNNING_QUEUE_EMPTY" and check
// for dirty nodes before running queries.
exports.runInitialQueries = async () => {
await runQueries()

active = true
await runQueries(true)
return
}

const runQueries = async () => {
const runQueries = async (initial = false) => {
// Don't run queries until bootstrap gets to "run graphql queries"
if (!active) {
return
}

// Find paths dependent on dirty nodes
queuedDirtyActions = _.uniq(queuedDirtyActions, a => a.payload.id)
const dirtyIds = findDirtyIds(queuedDirtyActions)
Expand All @@ -45,11 +49,25 @@ const runQueries = async () => {
const cleanIds = findIdsWithoutDataDependencies()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably need to restore this? For now


// Construct paths for all queries to run
const pathnamesToRun = _.uniq([
...runQueriesForPathnamesQueue,
...dirtyIds,
...cleanIds,
])
let pathnamesToRun = _.uniq([...dirtyIds, ...cleanIds])

// If this is the initial run, remove pathnames from `runQueriesForPathnamesQueue`
// if they're also not in the dirtyIds or cleanIds.
//
// We do this because the page component reducer/machine always
// adds pages to runQueriesForPathnamesQueue but during bootstrap
// we may not want to run those page queries if their data hasn't
// changed since the last time we ran Gatsby.
KyleAMathews marked this conversation as resolved.
Show resolved Hide resolved
let diffedPathnames = [...runQueriesForPathnamesQueue]
if (initial) {
diffedPathnames = _.intersection(
[...runQueriesForPathnamesQueue],
pathnamesToRun
)
}

// Combine.
pathnamesToRun = _.union(diffedPathnames, pathnamesToRun)

runQueriesForPathnamesQueue.clear()

Expand All @@ -58,6 +76,8 @@ const runQueries = async () => {
return
}

exports.runQueries = runQueries

emitter.on(`CREATE_NODE`, action => {
queuedDirtyActions.push(action)
})
Expand All @@ -66,12 +86,6 @@ emitter.on(`DELETE_NODE`, action => {
queuedDirtyActions.push({ payload: action.payload })
})

emitter.on(`CREATE_PAGE`, action => {
if (action.contextModified) {
exports.queueQueryForPathname(action.payload.path)
}
})

const runQueuedActions = async () => {
if (active && !running) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import filterContextForNode from "@gatsbyjs/relay-compiler/lib/filterContextForN
const _ = require(`lodash`)

import { store } from "../../redux"
const { boundActionCreators } = require(`../../redux/actions`)
import FileParser from "./file-parser"
import GraphQLIRPrinter from "@gatsbyjs/relay-compiler/lib/GraphQLIRPrinter"
import {
Expand Down Expand Up @@ -136,6 +137,7 @@ class Runner {
const compiledNodes: Queries = new Map()
const namePathMap = new Map()
const nameDefMap = new Map()
const nameErrorMap = new Map()
const documents = []

for (let [filePath, doc] of nodes.entries()) {
Expand Down Expand Up @@ -165,8 +167,18 @@ class Runner {
)
)
} catch (error) {
this.reportError(graphqlError(namePathMap, nameDefMap, error))
return compiledNodes
const { formattedMessage, docName, message, codeBlock } = graphqlError(
namePathMap,
nameDefMap,
error
)
nameErrorMap.set(docName, { formattedMessage, message, codeBlock })
boundActionCreators.queryExtractionGraphQLError({
componentPath: namePathMap.get(docName),
error: formattedMessage,
})
this.reportError(formattedMessage)
return false
}

// relay-compiler v1.5.0 added "StripUnusedVariablesTransform" to
Expand Down
24 changes: 23 additions & 1 deletion packages/gatsby/src/internal-plugins/query-runner/query-queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const Queue = require(`better-queue`)

const queryRunner = require(`./query-runner`)
const { store, emitter } = require(`../../redux`)
const { boundActionCreators } = require(`../../redux/actions`)
const websocketManager = require(`../../utils/websocket-manager`)

const processing = new Set()
Expand Down Expand Up @@ -55,20 +56,41 @@ const queue = new Queue((plObj, callback) => {
queue.push(waiting.get(plObj.id))
waiting.delete(plObj.id)
}

// Send event that the page query finished.
boundActionCreators.pageQueryRun({
path: plObj.id,
componentPath: plObj.componentPath,
isPage: plObj.isPage,
})

return callback(null, result)
},
error => callback(error)
)
}, queueOptions)

// HACKY!!! TODO: REMOVE IN NEXT REFACTOR
// We start paused until we call `runInitialQueries` during bootstrap.
let isBootstrapping = true
queue.pause()

emitter.on(`START_QUERY_QUEUE`, () => {
isBootstrapping = false
queue.resume()
})
// END HACKY

// Pause running queries when new nodes are added (processing starts).
emitter.on(`CREATE_NODE`, () => {
queue.pause()
})

// Resume running queries as soon as the api queue is empty.
emitter.on(`API_RUNNING_QUEUE_EMPTY`, () => {
queue.resume()
if (!isBootstrapping) {
queue.resume()
}
})

queue.on(`drain`, () => {
Expand Down