Skip to content

Commit bda1929

Browse files
jquenseKyleAMathews
authored andcommitted
require graphql import from gatsby to use (#6002)
* require graphql import * rollback breaking change * better runtime error * MOAR EDGE CASES! * Add suggestion in error to file an issue
1 parent 795c06f commit bda1929

File tree

6 files changed

+276
-91
lines changed

6 files changed

+276
-91
lines changed
Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`Leaves other graphql tags alone 1`] = `
4+
"import React from 'react';
5+
import { graphql } from 'relay';
6+
export default (() => React.createElement(\\"div\\", null, data.site.siteMetadata.title));
7+
export const query = graphql\`
8+
{
9+
site { siteMetadata { title }}
10+
}
11+
\`;"
12+
`;
13+
314
exports[`Transforms queries in <StaticQuery> 1`] = `
415
"import staticQueryData from \\"public/static/d/2626356014.json\\";
516
import React from 'react';
@@ -11,8 +22,14 @@ export default (() => React.createElement(StaticQuery, {
1122
}));"
1223
`;
1324
14-
exports[`Transforms queries in page components 1`] = `
15-
"import React from 'react';
16-
export default (() => React.createElement(\\"div\\", null, data.site.siteMetadata.title));
17-
export const query = \\"3227941719\\";"
18-
`;
25+
exports[`Transforms queries in page components 1`] = `"export const query = \\"3687030656\\";"`;
26+
27+
exports[`allows the global tag 1`] = `"export const query = \\"3687030656\\";"`;
28+
29+
exports[`handles import aliasing 1`] = `"export const query = \\"3687030656\\";"`;
30+
31+
exports[`handles require 1`] = `"export const query = \\"3687030656\\";"`;
32+
33+
exports[`handles require alias 1`] = `"export const query = \\"3687030656\\";"`;
34+
35+
exports[`handles require namespace 1`] = `"export const query = \\"3687030656\\";"`;

packages/babel-plugin-remove-graphql-queries/src/__tests__/index.js

Lines changed: 111 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,122 @@ const babel = require(`babel-core`)
22
const reactPreset = require(`@babel/preset-react`)
33
const plugin = require(`../`)
44

5-
var staticQuery = `
6-
import React from 'react'
7-
import { StaticQuery } from 'gatsby'
8-
9-
export default () => (
10-
<StaticQuery
11-
query={graphql\`{site { siteMetadata { title }}}\`}
12-
render={data => <div>{data.site.siteMetadata.title}</div>}
13-
/>
14-
)
15-
`
16-
17-
var pageComponent = `
18-
import React from 'react'
19-
20-
export default () => (
21-
<div>{data.site.siteMetadata.title}</div>
22-
)
23-
24-
export const query = graphql\`
25-
{
26-
site { siteMetadata { title }}
27-
}
28-
\`
29-
`
30-
31-
it(`Transforms queries in <StaticQuery>`, () => {
32-
const { code } = babel.transform(staticQuery, {
5+
function matchesSnapshot(query) {
6+
const { code } = babel.transform(query, {
337
presets: [reactPreset],
348
plugins: [plugin],
359
})
3610
expect(code).toMatchSnapshot()
11+
}
12+
13+
it(`Transforms queries in <StaticQuery>`, () => {
14+
matchesSnapshot(`
15+
import React from 'react'
16+
import { graphql, StaticQuery } from 'gatsby'
17+
18+
export default () => (
19+
<StaticQuery
20+
query={graphql\`{site { siteMetadata { title }}}\`}
21+
render={data => <div>{data.site.siteMetadata.title}</div>}
22+
/>
23+
)
24+
`)
3725
})
3826

3927
it(`Transforms queries in page components`, () => {
40-
const { code } = babel.transform(pageComponent, {
41-
presets: [reactPreset],
42-
plugins: [plugin],
43-
})
44-
expect(code).toMatchSnapshot()
28+
matchesSnapshot(`
29+
import { graphql } from 'gatsby'
30+
31+
export const query = graphql\`
32+
{
33+
site { siteMetadata { title }}
34+
}
35+
\`
36+
`)
37+
})
38+
39+
it(`allows the global tag`, () => {
40+
matchesSnapshot(
41+
`
42+
export const query = graphql\`
43+
{
44+
site { siteMetadata { title }}
45+
}
46+
\`
47+
`
48+
)
49+
})
50+
51+
it(`handles import aliasing`, () => {
52+
matchesSnapshot(
53+
`
54+
import { graphql as gql } from 'gatsby'
55+
56+
export const query = gql\`
57+
{
58+
site { siteMetadata { title }}
59+
}
60+
\`
61+
`
62+
)
63+
})
64+
65+
it(`handles require`, () => {
66+
matchesSnapshot(
67+
`
68+
const { graphql } = require('gatsby')
69+
70+
export const query = graphql\`
71+
{
72+
site { siteMetadata { title }}
73+
}
74+
\`
75+
`
76+
)
77+
})
78+
79+
it(`handles require namespace`, () => {
80+
matchesSnapshot(
81+
`
82+
const Gatsby = require('gatsby')
83+
84+
export const query = Gatsby.graphql\`
85+
{
86+
site { siteMetadata { title }}
87+
}
88+
\`
89+
`
90+
)
91+
})
92+
it(`handles require alias`, () => {
93+
matchesSnapshot(
94+
`
95+
const { graphql: gql } = require('gatsby')
96+
97+
export const query = gql\`
98+
{
99+
site { siteMetadata { title }}
100+
}
101+
\`
102+
`
103+
)
104+
})
105+
106+
it(`Leaves other graphql tags alone`, () => {
107+
matchesSnapshot(
108+
`
109+
import React from 'react'
110+
import { graphql } from 'relay'
111+
112+
export default () => (
113+
<div>{data.site.siteMetadata.title}</div>
114+
)
115+
116+
export const query = graphql\`
117+
{
118+
site { siteMetadata { title }}
119+
}
120+
\`
121+
`
122+
)
45123
})

packages/babel-plugin-remove-graphql-queries/src/index.js

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,93 @@ const graphql = require(`gatsby/graphql`)
33
const murmurhash = require(`./murmur`)
44
const nodePath = require(`path`)
55

6+
function getTagImport(tag) {
7+
const name = tag.node.name
8+
const binding = tag.scope.getBinding(name)
9+
10+
if (!binding) return null
11+
12+
const path = binding.path
13+
const parent = path.parentPath
14+
15+
if (
16+
binding.kind === `module` &&
17+
parent.isImportDeclaration() &&
18+
parent.node.source.value === `gatsby`
19+
)
20+
return path
21+
22+
if (
23+
path.isVariableDeclarator() &&
24+
path.get(`init`).isCallExpression() &&
25+
path.get(`init.callee`).isIdentifier({ name: `require` }) &&
26+
path.get(`init`).node.arguments[0].value === `gatsby`
27+
) {
28+
const id = path.get(`id`)
29+
if (id.isObjectPattern()) {
30+
return id
31+
.get(`properties`)
32+
.find(path => path.get(`value`).node.name === name)
33+
}
34+
return id
35+
}
36+
return null
37+
}
38+
39+
function isGraphqlTag(tag) {
40+
const isExpression = tag.isMemberExpression()
41+
const identifier = isExpression ? tag.get(`object`) : tag
42+
43+
const importPath = getTagImport(identifier)
44+
if (!importPath)
45+
return (
46+
tag.scope.hasGlobal(`graphql`) && tag.isIdentifier({ name: `graphql` })
47+
)
48+
49+
if (
50+
isExpression &&
51+
(importPath.isImportNamespaceSpecifier() || importPath.isIdentifier())
52+
) {
53+
return tag.get(`property`).node.name === `graphql`
54+
}
55+
56+
if (importPath.isImportSpecifier())
57+
return importPath.node.imported.name === `graphql`
58+
59+
if (importPath.isObjectProperty())
60+
return importPath.get(`key`).node.name === `graphql`
61+
62+
return false
63+
}
64+
65+
function removeImport(tag) {
66+
const isExpression = tag.isMemberExpression()
67+
const identifier = isExpression ? tag.get(`object`) : tag
68+
const importPath = getTagImport(identifier)
69+
70+
if (!importPath) return
71+
72+
const parent = importPath.parentPath
73+
74+
if (importPath.isImportSpecifier()) {
75+
if (parent.node.specifiers.length === 1) parent.remove()
76+
else importPath.remove()
77+
}
78+
if (importPath.isObjectProperty()) {
79+
if (parent.node.properties.length === 1)
80+
importPath.findParent(p => p.isVariableDeclaration())?.remove()
81+
else importPath.remove()
82+
}
83+
if (importPath.isIdentifier()) {
84+
importPath.findParent(p => p.isVariableDeclaration())?.remove()
85+
}
86+
}
87+
688
function getGraphQLTag(path) {
789
const tag = path.get(`tag`)
8-
if (!tag.isIdentifier({ name: `graphql` })) return {}
90+
const isGlobal = tag.scope.hasGlobal(`graphql`)
91+
92+
if (!isGlobal && !isGraphqlTag(tag)) return {}
993

1094
const quasis = path.node.quasi.quasis
1195

@@ -26,7 +110,7 @@ function getGraphQLTag(path) {
26110
if (ast.definitions.length === 0) {
27111
throw new Error(`BabelPluginRemoveGraphQL: Unexpected empty graphql tag.`)
28112
}
29-
return { ast, text, hash }
113+
return { ast, text, hash, isGlobal }
30114
} catch (err) {
31115
throw new Error(
32116
`BabelPluginRemoveGraphQLQueries: GraphQL syntax error in query:\n\n${text}\n\nmessage:\n\n${
@@ -44,7 +128,8 @@ export default function({ types: t }) {
44128
JSXIdentifier(path2) {
45129
if (
46130
[`production`, `test`].includes(process.env.NODE_ENV) &&
47-
path2.isJSXIdentifier({ name: `StaticQuery` })
131+
path2.isJSXIdentifier({ name: `StaticQuery` }) &&
132+
path2.referencesImport(`gatsby`)
48133
) {
49134
const identifier = t.identifier(`staticQueryData`)
50135
const filename = state.file.opts.filename
@@ -79,13 +164,16 @@ export default function({ types: t }) {
79164

80165
path.traverse({
81166
TaggedTemplateExpression(path2, state) {
82-
const { ast, text, hash } = getGraphQLTag(path2)
167+
const { ast, text, hash, isGlobal } = getGraphQLTag(path2)
83168

84169
if (!ast) return null
85170

86171
const queryHash = hash.toString()
87172
const query = text
88173

174+
const tag = path2.get(`tag`)
175+
if (!isGlobal) removeImport(tag)
176+
89177
// Replace the query with the hash of the query.
90178
path2.replaceWith(t.StringLiteral(queryHash))
91179

packages/gatsby/src/cache-dir/gatsby-browser-entry.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,20 @@ StaticQuery.propTypes = {
4545
render: PropTypes.func.isRequired,
4646
}
4747

48+
function graphql() {
49+
throw new Error(
50+
`It appears like Gatsby is misconfigured. Gatsby related \`graphql\` calls ` +
51+
`are supposed to only be evaluated at compile time, and then compiled away,. ` +
52+
`Unfortunately, something went wrong and the query was left in the compiled code.\n\n.` +
53+
`Unless your site has a complex or custom babel/Gatsby configuration this is likely a bug in Gatsby.` +
54+
`Please file an issue and we'll work together to resolve this.`
55+
)
56+
}
57+
4858
export {
4959
Link,
5060
withPrefix,
61+
graphql,
5162
push,
5263
replace,
5364
navigateTo, // TODO: remove navigateTo for v3

0 commit comments

Comments
 (0)