GraphQL错误:字段“ image”必须没有选择,因为类型“ String”没有子字段

时间:2019-11-25 06:10:29

标签: graphql gatsby netlify netlify-cms

说明

我是新手,正在学习GatsbyJs / NetlifyCM / GraphQL,所以很抱歉我遇到这么愚蠢的人。但是,我已经尝试解决此问题已有5天了,我不知所措。我正在尝试将NetlifyCMS实施到GatsbyJS博客启动程序中。除了图像,我似乎一切正常。 Graphql正在以“ img / image.jpg”查询图像URL,这是在MD文件中设置的,并且效果很好。但是,当我使用NetlifyCMS发表新文章时,将URL设置为“ /img/image.jpg”,而graphql似乎找不到它。前面的多余斜杠会导致错误。如果我删除斜线或在斜线前面加点“ ./img/image.jpg”,它会很好地显示,使我相信相对路径可能有效吗?我已经阅读了issue 4123issue 13938issue 5990,以及其他多个类似的问题和博客,但仍然无法正常工作。我尝试实现 gatsby-remark-relative-images gatsby-plugin-netlify-cms ,但似乎都无法解决。

任何帮助将不胜感激。我是Web开发的新手,对gatsby / graphql还是很新。谢谢。

复制步骤

在MD文件中的图像URL前面删除或添加/。这是克隆/下载https://github.com/AaronCuddeback/blog.aaroncuddeback.com

的仓库的链接。

环境

System:
    OS: Windows 10
    CPU: (16) x64 Intel(R) Core(TM) i9-9900KF CPU @ 3.60GHz
  Binaries:
    Node: 12.13.0 - C:\Program Files\nodejs\node.EXE
    npm: 6.13.0 - ~\AppData\Roaming\npm\npm.CMD
  Languages:
    Python: 2.7.17 - /usr/bin/python
  Browsers:
    Edge: 44.18362.387.0
  npmPackages:
    gatsby: 2.18.2 => 2.18.2 
    gatsby-image: 2.2.33 => 2.2.33 
    gatsby-plugin-canonical-urls: 2.1.15 => 2.1.15 
    gatsby-plugin-emotion: 4.1.15 => 4.1.15 
    gatsby-plugin-feed: 2.3.21 => 2.3.21 
    gatsby-plugin-google-analytics: 2.1.28 => 2.1.28 
    gatsby-plugin-netlify: ^2.1.25 => 2.1.25 
    gatsby-plugin-netlify-cms: ^4.1.28 => 4.1.28 
    gatsby-plugin-netlify-cms-paths: ^1.3.0 => 1.3.0 
    gatsby-plugin-postcss: 2.1.15 => 2.1.15 
    gatsby-plugin-react-helmet: 3.1.15 => 3.1.15 
    gatsby-plugin-sharp: 2.3.2 => 2.3.2 
    gatsby-plugin-sitemap: 2.2.21 => 2.2.21 
    gatsby-plugin-typescript: 2.1.19 => 2.1.19 
    gatsby-remark-abbr: 2.0.0 => 2.0.0 
    gatsby-remark-copy-linked-files: 2.1.30 => 2.1.30 
    gatsby-remark-images: 3.1.33 => 3.1.33 
    gatsby-remark-prismjs: 3.3.24 => 3.3.24 
    gatsby-remark-relative-images: ^0.2.3 => 0.2.3 
    gatsby-remark-responsive-iframe: 2.2.27 => 2.2.27 
    gatsby-remark-smartypants: 2.1.16 => 2.1.16 
    gatsby-source-filesystem: 2.1.38 => 2.1.38 
    gatsby-transformer-json: 2.2.19 => 2.2.19 
    gatsby-transformer-remark: 2.6.37 => 2.6.37 
    gatsby-transformer-sharp: 2.3.5 => 2.3.5 
    gatsby-transformer-yaml: 2.2.17 => 2.2.17 

文件内容:

gatsby-config.js:

const path = require('path');

module.exports = {
  siteMetadata: {
    title: 'Ghost',
    description: 'The professional publishing platform',
    siteUrl: 'https://gatsby-casper.netlify.com', // full path to blog - no ending slash
  },
  mapping: {
    'MarkdownRemark.frontmatter.author': 'AuthorYaml',
  },
  plugins: [
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        path: `${__dirname}/static/img`,
        name: 'uploads',
      },
    },
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        path: `${__dirname}/src/content`,
        name: 'content',
      },
    },
    {
      resolve: `gatsby-plugin-netlify-cms-paths`,
      options: {
        cmsConfig: `/static/admin/config.yml`,
      },
    },
    'gatsby-plugin-sitemap',
    'gatsby-plugin-sharp',
    {
      resolve: 'gatsby-transformer-remark',
      options: {
        plugins: [
          `gatsby-plugin-netlify-cms-paths`,
          {
            resolve: `gatsby-remark-relative-images`,
            options: {
              name: 'uploads', // Must match the source name ^
            },
          },
          {
            resolve: 'gatsby-remark-images',
            options: {
              maxWidth: 1170,
              quality: 90,
            },
          },
          {
            resolve: 'gatsby-remark-responsive-iframe',
            options: {
              wrapperStyle: 'margin-bottom: 1rem',
            },
          },
          'gatsby-remark-prismjs',
          'gatsby-remark-copy-linked-files',
          'gatsby-remark-smartypants',
          'gatsby-remark-abbr',
        ],
      },
    },
    'gatsby-transformer-json',
    {
      resolve: 'gatsby-plugin-canonical-urls',
      options: {
        siteUrl: 'https://gatsby-casper.netlify.com',
      },
    },
    'gatsby-plugin-emotion',
    'gatsby-plugin-typescript',
    'gatsby-transformer-sharp',
    'gatsby-plugin-react-helmet',
    'gatsby-transformer-yaml',
    'gatsby-plugin-feed',
    {
      resolve: 'gatsby-plugin-postcss',
      options: {
        postCssPlugins: [require('postcss-color-function'), require('cssnano')()],
      },
    },
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        trackingId: 'UA-XXXX-Y',
        // Puts tracking script in the head instead of the body
        head: true,
        // IP anonymization for GDPR compliance
        anonymize: true,
        // Disable analytics for users with `Do Not Track` enabled
        respectDNT: true,
        // Avoids sending pageview hits from custom paths
        exclude: ['/preview/**'],
        // Specifies what percentage of users should be tracked
        sampleRate: 100,
        // Determines how often site speed tracking beacons will be sent
        siteSpeedSampleRate: 10,
      },
    },
    `gatsby-plugin-netlify-cms`,
    `gatsby-plugin-netlify`,
  ],
};

gatsby-node.js

const path = require('path');
const _ = require('lodash');
const { fmImagesToRelative } = require('gatsby-remark-relative-images');

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions;
  fmImagesToRelative(node);

  // Sometimes, optional fields tend to get not picked up by the GraphQL
  // interpreter if not a single content uses it. Therefore, we're putting them
  // through `createNodeField` so that the fields still exist and GraphQL won't
  // trip up. An empty string is still required in replacement to `null`.
  switch (node.internal.type) {
    case 'MarkdownRemark': {
      const { permalink, layout, primaryTag } = node.frontmatter;
      const { relativePath } = getNode(node.parent);

      let slug = permalink;

      if (!slug) {
        slug = `/${relativePath.replace('.md', '')}/`;
      }

      // Used to generate URL to view this content.
      createNodeField({
        node,
        name: 'slug',
        value: slug || '',
      });

      // Used to determine a page layout.
      createNodeField({
        node,
        name: 'layout',
        value: layout || '',
      });

      createNodeField({
        node,
        name: 'primaryTag',
        value: primaryTag || '',
      });
    }
  }
};

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  const result = await graphql(`
    {
      allMarkdownRemark(
        limit: 2000
        sort: { fields: [frontmatter___date], order: ASC }
        filter: { frontmatter: { draft: { ne: true } } }
      ) {
        edges {
          node {
            excerpt
            timeToRead
            frontmatter {
              title
              tags
              date
              draft
              image {
                childImageSharp {
                  fluid(maxWidth: 3720) {
                    aspectRatio
                    base64
                    sizes
                    src
                    srcSet
                  }
                }
                publicURL
              }
              author {
                id
                bio
                avatar {
                  children {
                    ... on ImageSharp {
                      fixed(quality: 90) {
                        src
                      }
                    }
                  }
                }
              }
            }
            fields {
              layout
              slug
            }
          }
        }
      }
      allAuthorYaml {
        edges {
          node {
            id
          }
        }
      }
    }
  `);

  if (result.errors) {
    console.error(result.errors);
    throw new Error(result.errors);
  }

  // Create post pages
  const posts = result.data.allMarkdownRemark.edges;

  // Create paginated index
  const postsPerPage = 6;
  const numPages = Math.ceil(posts.length / postsPerPage);

  Array.from({ length: numPages }).forEach((_, i) => {
    createPage({
      path: i === 0 ? '/' : `/${i + 1}`,
      component: path.resolve('./src/templates/index.tsx'),
      context: {
        limit: postsPerPage,
        skip: i * postsPerPage,
        numPages,
        currentPage: i + 1,
      },
    });
  });

  posts.forEach(({ node }, index) => {
    const { slug, layout } = node.fields;
    const prev = index === 0 ? null : posts[index - 1].node;
    const next = index === posts.length - 1 ? null : posts[index + 1].node;

    createPage({
      path: slug,
      // This will automatically resolve the template to a corresponding
      // `layout` frontmatter in the Markdown.
      //
      // Feel free to set any `layout` as you'd like in the frontmatter, as
      // long as the corresponding template file exists in src/templates.
      // If no template is set, it will fall back to the default `post`
      // template.
      //
      // Note that the template has to exist first, or else the build will fail.
      component: path.resolve(`./src/templates/${layout || 'post'}.tsx`),
      context: {
        // Data passed to context is available in page queries as GraphQL variables.
        slug,
        prev,
        next,
        primaryTag: node.frontmatter.tags ? node.frontmatter.tags[0] : '',
      },
    });
  });

  // Create tag pages
  const tagTemplate = path.resolve('./src/templates/tags.tsx');
  const tags = _.uniq(
    _.flatten(
      result.data.allMarkdownRemark.edges.map(edge => {
        return _.castArray(_.get(edge, 'node.frontmatter.tags', []));
      }),
    ),
  );
  tags.forEach(tag => {
    createPage({
      path: `/tags/${_.kebabCase(tag)}/`,
      component: tagTemplate,
      context: {
        tag,
      },
    });
  });

  // Create author pages
  const authorTemplate = path.resolve('./src/templates/author.tsx');
  result.data.allAuthorYaml.edges.forEach(edge => {
    createPage({
      path: `/author/${_.kebabCase(edge.node.id)}/`,
      component: authorTemplate,
      context: {
        author: edge.node.id,
      },
    });
  });
};

exports.onCreateWebpackConfig = ({ stage, actions }) => {
  // adds sourcemaps for tsx in dev mode
  if (stage === `develop` || stage === `develop-html`) {
    actions.setWebpackConfig({
      devtool: 'eval-source-map',
    });
  }
};

0 个答案:

没有答案