gatsbyjs / gatsby

The best React-based framework with performance, scalability and security built in.
https://www.gatsbyjs.com
MIT License
55.3k stars 10.31k forks source link

gatsby build fails when StaticQuery has closing tag #6139

Closed mjmeintjes closed 6 years ago

mjmeintjes commented 6 years ago

Description

When I use <StaticQuery></StaticQuery> gatsby build fails. When I use <StaticQuery />, everything works.

Failure message:

Error: ./src/components/layout.js
  Module build failed (from ./node_modules/babel-loader/lib/index.js):
  TypeError: Cannot read property 'push' of undefined

This happens at the following location: https://github.com/gatsbyjs/gatsby/blob/f41eb2a58d8a8d5e84663bb6a9080aa6b3d530df/packages/babel-plugin-remove-graphql-queries/src/index.js#L139

The value of path2.parent is:

Node {
  type: 'JSXClosingElement',
  start: 1095,
  end: 1109,
  loc:
   SourceLocation {
     start: Position { line: 38, column: 8 },
     end: Position { line: 38, column: 22 } },
  name:
   Node {
     type: 'JSXIdentifier',
     start: 1097,
     end: 1108,
     loc: SourceLocation { start: [Position], end: [Position] },
     name: 'StaticQuery' } }

Environment

System: OS: Linux 4.13 Ubuntu 17.10 (Artful Aardvark) CPU: x64 AMD A10-6700 APU with Radeon(tm) HD Graphics Shell: 5.2 - /usr/bin/zsh Binaries: Node: 9.10.1 - /usr/local/bin/node Yarn: 1.6.0 - /usr/local/bin/yarn npm: 5.8.0 - /usr/local/bin/npm Browsers: Chrome: 65.0.3325.162 Firefox: 60.0.2 npmPackages: gatsby: next => 2.0.0-beta.9 gatsby-plugin-react-helmet: next => 3.0.0-beta.2 gatsby-plugin-sharp: next => 2.0.0-beta.2 gatsby-plugin-typography: next => 2.2.0-beta.2 gatsby-remark-component: ^1.1.3 => 1.1.3 gatsby-remark-copy-linked-files: next => 2.0.0-beta.2 gatsby-remark-emojis: ^0.2.3 => 0.2.3 gatsby-remark-generic-extensions: ^0.0.1 => 0.0.1 gatsby-remark-images: next => 2.0.1-beta.3 gatsby-remark-smartypants: ^1.4.12 => 1.4.12 gatsby-source-filesystem: next => 2.0.1-beta.3 gatsby-transformer-remark: next => 2.1.1-beta.2

File contents (if changed)

gatsby-config.js: N/A package.json: N/A gatsby-node.js: N/A gatsby-browser.js: N/A gatsby-ssr.js: N/A

LekoArts commented 6 years ago

Please post the relevant code where you use StaticQuery

mjmeintjes commented 6 years ago

Didn't work:

import React from 'react'
import Helmet from 'react-helmet'
import { StaticQuery, graphql } from "gatsby"
import Header from '../components/header'
import './layout.css'

export default ({ children }) => (
    <StaticQuery
      query={graphql`
  query SiteTitleQuery {
    site {
      siteMetadata {
        title
        description
        keywords
      }
    }
  }
      `}
      render={data=> (
          <>
            <Helmet
              title={data.site.siteMetadata.title}
              meta={[
                  { name: 'description', content: data.site.siteMetadata.description },
                  { name: 'keywords', content: data.site.siteMetadata.keywords },
              ]}
              >
              <link rel="stylesheet" type="text/css" href="https://unpkg.com/tachyons@4.10.0/css/tachyons.min.css"/>
            </Helmet>
            <Header siteTitle={data.site.siteMetadata.title} />
            <div className="measure-wide center ph2">
              {children}
            </div>
          </>
      )}>
    </StaticQuery>
)

Worked:

import React from 'react'
import Helmet from 'react-helmet'
import { StaticQuery, graphql } from "gatsby"
import Header from '../components/header'
import './layout.css'

export default ({ children }) => (
    <StaticQuery
      query={graphql`
  query SiteTitleQuery {
    site {
      siteMetadata {
        title
        description
        keywords
      }
    }
  }
      `}
      render={data=> (
          <>
            <Helmet
              title={data.site.siteMetadata.title}
              meta={[
                  { name: 'description', content: data.site.siteMetadata.description },
                  { name: 'keywords', content: data.site.siteMetadata.keywords },
              ]}
              >
              <link rel="stylesheet" type="text/css" href="https://unpkg.com/tachyons@4.10.0/css/tachyons.min.css"/>
            </Helmet>
            <Header siteTitle={data.site.siteMetadata.title} />
            <div className="measure-wide center ph2">
              {children}
            </div>
          </>
      )} />
)
LekoArts commented 6 years ago

Yeah, that won't work. render is the property of <StaticQuery /> and therefore it only needs a self-closing bracket. If you want to wrap your content you'll need to pass children to the component. https://reactjs.org/docs/render-props.html#using-props-other-than-render

Because in your upper example you're writing <StaticQuery render={}></StaticQuery. Do you have eslint enabled? Because this would give you an error telling you to either pass children to the component or use a /> instead.

swyxio commented 6 years ago

i wonder if we can put in a warning when there is no self closing bracket.

mjmeintjes commented 6 years ago

i wonder if we can put in a warning when there is no self closing bracket.

That is what I was thinking. It was pretty confusing when I got the error, and required me to dig into the underlying js files to diagnose.

LekoArts commented 6 years ago

Do you have eslint enabled? Because this would give you an error telling you to either pass children to the component or use a /> instead.

^this

m-allanson commented 6 years ago

👍 to eslint.

I'm going to mark this as a bug also, Gatsby should either handle <StaticQuery></StaticQuery> or provide a more helpful error message when there's no self-closing bracket.

swyxio commented 6 years ago

im interested to help fix this but i have no idea where to start. i vaguely know that it's in the query compiler. any specific place you would point me to?

MichaelZoerner commented 6 years ago

@sw-yx You could probably start around here. To play around with ASTs this could help. You will probably land here. Good luck!

swyxio commented 6 years ago

awesome. yea I started messing around with astexplorer last night and have been on an ast learning spree for the past few days. I'm modding astexplorer so I can declaratively write out what I want the Before and After to look like and then it shows me the diff in the ASTs (unless it does that already and I don't know about it..?

mjmeintjes commented 6 years ago

Just wanted to add the following:

swyxio commented 6 years ago

This only seems to happen with gatsby build, not gatsby develop

this sounds very weird to me. it should work the same way both times. I also found a possibly related but when the graphql query is split up from the StaticQuery tag: https://swizec.com/blog/upgrading-gatsby-v2-hivemind/swizec/8481 this definitely happens in Gatsby develop

swyxio commented 6 years ago

I have a bunch of personal things to take care of and am feeling a bit sick right now so just in case anyone wants to try fixing this, pls do

keplersj commented 6 years ago

I just encountered this same issue, except in a slightly different variant. I saw that <StaticQuery> was using a render prop, so I figured that it would also accept children as a function like most other render prop components. This worked with gatsby develop, but broke on gatsby build. Moving the function into the render prop of a self-closing <StaticQuery/> component fixed this.

deadcoder0904 commented 6 years ago

Same error. No warning when I try gatsby develop but throws error when done gatsby build

This is the error -

Error: ./src/components/layout.js
  Module build failed (from ./node_modules/babel-loader/lib/index.js):
  TypeError: Cannot read property 'push' of undefined

And this is my ./src/components/layout.js -

import React from "react";
import Helmet from "react-helmet";
import { StaticQuery, graphql, Link } from "gatsby";

const Layout = ({ children, data }) => (
  <StaticQuery
    query={graphql`
      query SiteTitleQuery {
        site {
          siteMetadata {
            title
          }
        }
      }
    `}
  >
    {data => (
      <>
        <Helmet
          title={data.site.siteMetadata.title}
          meta={[
            { name: "description", content: "Sample" },
            { name: "keywords", content: "sample, something" }
          ]}
        />
        {children}
      </>
    )}
  </StaticQuery>
);

export default Layout;

But works if I use it by passing render as a prop like -

import React from "react";
import Helmet from "react-helmet";
import { StaticQuery, graphql, Link } from "gatsby";

const Layout = ({ children, data }) => (
  <StaticQuery
    query={graphql`
      query SiteTitleQuery {
        site {
          siteMetadata {
            title
          }
        }
      }
    `}
    render={data => (
      <>
        <Helmet
          title={data.site.siteMetadata.title}
          meta={[
            { name: "description", content: "Sample" },
            { name: "keywords", content: "sample, something" }
          ]}
        />
        {children}
      </>
    )}
  />
);

export default Layout;
joshdcuneo commented 6 years ago

I may be wrong @deadcoder0904 but I my understanding is that the StaticQuery component receives the data prop so you could remove the data in this line since it doesn't do anything.

const Layout = ({ children, data }) => (

The other components (Helment etc) can only access 'data' when they are rendered within StaticQuery because 'data' is passed as a prop to StaticQuery not Layout.

Sorry if that is a bad explanation, hopefully it still helps to clarify!

deadcoder0904 commented 6 years ago

@joshdcuneo Yeah thanks. I actually copied v1 example I guess or just missed it Idk. Good catch :)

joshdcuneo commented 6 years ago

No dramas. I did the same a couple of days ago I'm pretty sure. :-D

resir014 commented 6 years ago

Hi, I'm encountering the same issue. Using children props in <StaticQuery> will make the site fail when building (i.e. running gatsby build).

gatsby develop still works normally.

// this doesn't work...

const IndexLayout: React.SFC = ({ children }) => (
  <LayoutRoot>
    <StaticQuery
      query={graphql`
        query IndexLayoutQuery {
          site {
            siteMetadata {
              title
              description
            }
          }
        }
      `}
    >
      {(data: StaticQueryProps) => (
        <React.Fragment>
          <Helmet
            title={data.site.siteMetadata.title}
            meta={[
              { name: 'description', content: data.site.siteMetadata.description },
              { name: 'keywords', content: 'gatsbyjs, gatsby, javascript, sample, something' }
            ]}
          />
          <Header title={data.site.siteMetadata.title} />
        </React.Fragment>
      )}
    </StaticQuery>
    <LayoutMain>{children}</LayoutMain>
  </LayoutRoot>
)
// ...but this does

const IndexLayout: React.SFC = ({ children }) => (
  <LayoutRoot>
    <StaticQuery
      query={graphql`
        query IndexLayoutQuery {
          site {
            siteMetadata {
              title
              description
            }
          }
        }
      `}
      render={(data: StaticQueryProps) => (
        <React.Fragment>
          <Helmet
            title={data.site.siteMetadata.title}
            meta={[
              { name: 'description', content: data.site.siteMetadata.description },
              { name: 'keywords', content: 'gatsbyjs, gatsby, javascript, sample, something' }
            ]}
          />
          <Header title={data.site.siteMetadata.title} />
        </React.Fragment>
      )}
    />
    <LayoutMain>{children}</LayoutMain>
  </LayoutRoot>
)

I'm using TypeScript, not sure if that might be the problem.

resir014 commented 6 years ago

I also have to add that replacing render with children, e.g. <StaticQuery query={...} children={...} /> also works. So I think it might have to do with the StaticQuery component being a self-closing element or not.