Skip to content

Commit

Permalink
fix(gatsby): Multiple root query error (#36525)
Browse files Browse the repository at this point in the history
  • Loading branch information
marvinjude committed Sep 19, 2022
1 parent b029564 commit a922943
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 33 deletions.
2 changes: 1 addition & 1 deletion docs/docs/how-to/routing/mdx.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ For further reading, check out the [createPages](/docs/reference/config-files/ga

### Make a layout template for your posts

You can create a file called `posts.jsx` in `src/templates` - this component will be rendered as the template for all posts. Now, create a component that accepts your compiled MDX content via `children` and uses GraphQL `data` to show the title. **Please note:** The query must **not** have a name. This way Gatsby can create a unique one internally and the `__contentFilePath` in the next step will work correctly.
You can create a file called `posts.jsx` in `src/templates` - this component will be rendered as the template for all posts. Now, create a component that accepts your compiled MDX content via `children` and uses GraphQL `data` to show the title:

```jsx:title=src/templates/posts.jsx
import React from "react"
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/mdx/src/pages/fs-api/{Mdx.fields__slug}.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function FSAPIComponent(props) {
}

export const query = graphql`
query($id: String) {
query SomeQueryName($id: String) {
mdx(id: { eq: $id }) {
internal {
contentFilePath
Expand Down
6 changes: 2 additions & 4 deletions packages/gatsby-plugin-mdx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,6 @@ However, we'd recommend placing such files into the `src/pages` directory and `g

1. Instead of querying for the `body` on the MDX node, the page template now receives the transformed MDX as `children` property.
1. You no longer need to use `<MDXRenderer>` as you can use `{children}` directly.
1. If you have given your query a name (e.g. `query PostTemplate`) you have to remove the name. Gatsby automatically creates a name internally, but if a name is provided you'll see a "Duplicate query" warning.

```diff
import React from "react"
Expand All @@ -686,9 +685,8 @@ import { graphql } from "gatsby"
)
}

export const pageQuery = graphql`
- query PostTemplate($id: String!) {
+ query ($id: String!) {
export const pageQuery = graphql`
query PostTemplate($id: String!) {
mdx(id: { eq: $id }) {
frontmatter {
title
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Map {
id
}",
"path": "mockFile",
"templatePath": "mockFile",
"text": "fragment PostsJsonFragment on PostsJson {
id
}
Expand Down Expand Up @@ -51,6 +52,7 @@ Object {
}
}",
"path": "mockFile",
"templatePath": "mockFile",
"text": "fragment PostsJsonFragment on PostsJson {
id
}
Expand Down Expand Up @@ -85,6 +87,7 @@ Object {
id
}",
"path": "mockFile",
"templatePath": "mockFile",
"text": "fragment PostsJsonFragment on PostsJson {
id
}
Expand Down Expand Up @@ -115,6 +118,7 @@ Object {
}
}",
"path": "mockFile",
"templatePath": "mockFile",
"text": "query mockFileQuery {
allPostsJson {
nodes {
Expand Down Expand Up @@ -142,6 +146,7 @@ Object {
}
}",
"path": "mockFile",
"templatePath": "mockFile",
"text": "query mockFileQuery {
allPostsJson {
nodes {
Expand Down Expand Up @@ -209,6 +214,7 @@ Map {
}
}",
"path": "mockFile",
"templatePath": "mockFile",
"text": "query mockFileQuery {
allPostsJson {
nodes {
Expand Down Expand Up @@ -236,6 +242,7 @@ Object {
}
}",
"path": "mockFile",
"templatePath": "mockFile",
"text": "fragment PostsJsonFragment on PostsJson {
id
...AnotherPostsJsonFragment
Expand Down Expand Up @@ -279,6 +286,7 @@ Object {
id
}",
"path": "mockFile",
"templatePath": "mockFile",
"text": "fragment PostsJsonFragment on PostsJson {
id
}
Expand Down
4 changes: 4 additions & 0 deletions packages/gatsby/src/query/__tests__/query-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ describe(`actual compiling`, () => {
"name": "page",
"originalText": "query page { allPostsJson { nodes { id } } }",
"path": "mockFile",
"templatePath": "mockFile",
"text": "query page {
allPostsJson {
nodes {
Expand Down Expand Up @@ -461,6 +462,7 @@ describe(`actual compiling`, () => {
}
}",
"path": "mockFile1",
"templatePath": "mockFile1",
"text": "fragment Foo on Directory {
id
}
Expand Down Expand Up @@ -497,6 +499,7 @@ describe(`actual compiling`, () => {
}
}",
"path": "mockFile2",
"templatePath": "mockFile2",
"text": "fragment Bar on Directory {
parent {
...Foo
Expand Down Expand Up @@ -1126,6 +1129,7 @@ describe(`actual compiling`, () => {
}
}",
"path": "mockFile",
"templatePath": "mockFile",
"text": "query mockFileQuery {
allPostsJson {
nodes {
Expand Down
69 changes: 42 additions & 27 deletions packages/gatsby/src/query/query-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const { store } = require(`../redux`)
import { actions } from "../redux/actions"

import { websocketManager } from "../utils/websocket-manager"
import { getPathToLayoutComponent } from "gatsby-core-utils"
const { default: FileParser } = require(`./file-parser`)
const {
graphqlError,
Expand Down Expand Up @@ -236,7 +237,12 @@ const extractOperations = (schema, parsedQueries, addError, parentSpan) => {
const name = def.name.value
let printedAst = null
if (def.kind === Kind.OPERATION_DEFINITION) {
operations.push(def)
operations.push({
def,
filePath,
templatePath: getPathToLayoutComponent(filePath),
hash,
})
} else if (def.kind === Kind.FRAGMENT_DEFINITION) {
// Check if we already registered a fragment with this name
printedAst = print(def)
Expand Down Expand Up @@ -277,6 +283,7 @@ const extractOperations = (schema, parsedQueries, addError, parentSpan) => {
isConfigQuery,
isFragment: def.kind === Kind.FRAGMENT_DEFINITION,
hash: hash,
templatePath: getPathToLayoutComponent(filePath),
})
})
}
Expand All @@ -303,31 +310,9 @@ const processDefinitions = ({
.map(([name, _]) => name)

for (const operation of operations) {
const name = operation.name.value
const { filePath, templatePath, def } = operation
const name = def.name.value
const originalDefinition = definitionsByName.get(name)
const filePath = definitionsByName.get(name).filePath

// Check for duplicate page/static queries in the same component.
// (config query is not a duplicate of page/static query in the component)
// TODO: make sure there is at most one query type per component (e.g. one config + one page)
if (processedQueries.has(filePath) && !originalDefinition.isConfigQuery) {
const otherQuery = processedQueries.get(filePath)

addError(
multipleRootQueriesError(
filePath,
originalDefinition.def,
otherQuery && definitionsByName.get(otherQuery.name).def
)
)

store.dispatch(
actions.queryExtractionGraphQLError({
componentPath: filePath,
})
)
continue
}

const { usedFragments, missingFragments } =
determineUsedFragmentsForDefinition(
Expand Down Expand Up @@ -359,10 +344,11 @@ const processDefinitions = ({
kind: Kind.DOCUMENT,
definitions: Array.from(usedFragments.values())
.map(name => definitionsByName.get(name).def)
.concat([operation]),
.concat([operation.def]),
}

const errors = validate(schema, document)

if (errors && errors.length) {
for (const error of errors) {
const { formattedMessage, message } = graphqlError(
Expand Down Expand Up @@ -396,14 +382,43 @@ const processDefinitions = ({
continue
}

const printedDocument = print(document)
// Check for duplicate page/static queries in the same component.
// (config query is not a duplicate of page/static query in the component)
// TODO: make sure there is at most one query type per component (e.g. one config + one page)
if (processedQueries.has(filePath) && !originalDefinition.isConfigQuery) {
const otherQuery = processedQueries.get(filePath)

if (
templatePath !== otherQuery.templatePath ||
printedDocument !== otherQuery.text
) {
addError(
multipleRootQueriesError(
filePath,
originalDefinition.def,
otherQuery && definitionsByName.get(otherQuery.name).def
)
)

store.dispatch(
actions.queryExtractionGraphQLError({
componentPath: filePath,
})
)
continue
}
}

const query = {
name,
text: print(document),
text: printedDocument,
originalText: originalDefinition.text,
path: filePath,
isHook: originalDefinition.isHook,
isStaticQuery: originalDefinition.isStaticQuery,
isConfigQuery: originalDefinition.isConfigQuery,
templatePath,
// ensure hash should be a string and not a number
hash: String(originalDefinition.hash),
}
Expand Down

0 comments on commit a922943

Please sign in to comment.