Плагин Gatsby Invariant Violation при использовании createRemoveFileNode для добавления изображений

Я создал плагин, который использует gatsby-source-shopify для получения обзоров продуктов Stamped.io для сайтов Gatsby. Эта проблема почти такая же, как в этой проблеме, за исключением того, что в моем onCreateNode, где я использую createRemoveFileNode, я запускаю цикл, создающий localFile для каждого изображения в массиве.

Первый gatsby develop или gatsby build всегда будет работать нормально, но для повторного выполнения любой из команд необходимо запустить gatsby clean, так как требуется очистка кеша. Это проблема для инкрементальных сборок.

  • Я неправильно создаю свои типы?
  • Не следует ли мне использовать вывод типа и вместо этого создать более надежную схему? Если так, то я не совсем понимаю, как это сделать.

Я получаю вот такую ​​ошибку:

Encountered an error trying to infer a GraphQL type for: `localFile___NODE`. There is no corresponding node with the `id` field matching: "4b1814d2-7b9b-5233-96a7-d7314a5f45e2,61d23e80-5848-5ba6-82a6-197ab82feb2f,d6ce8973-4f1f-5320-853b-9a2f0e4d9f91,99629f80-a6de-5ac3-aac0-fd09a4090a2a,f477a7d5-5e8f-5ec9-a218-b15b5bbeae22,e0159666-de4a-5fc3-bc30-f4725371533d,7c7b7bd0-d225-5455-a6dd-6dcbd9b811df,75f5db4f-fbc8-53cf-9a8e-7aff7551d136,eb7a0987-7273-5ba5-9508-36c7dc7c28a8,bdbb0713-0e22-5508-bbd3-a055c151e297,5658a7c8-b
92f-5f38-8d30-41a948a5ced6,9c73c510-2572-53ea-aa6f-1147f2eedccd,e3be0651-3efd-5894-827a-a78bee74f6c6,3c345ab0-309b-5509-8ea2-10685df78f72,0f317b0f-fde2-5a8e-8c75-4b6e38b7033e,5511536c-de3f-5374-9fd7-2e72996e55da,b15caed3-b448-5f7a-9773-b624a79f344e,b7eeaaac-4ac1-504e-9e13-abc2ce0cb4a4,2b72d7a5-d9b1-5ef0-b5dc-272e7987ef24,6a4cd9a6-4a4a-5603-80de-3429199c5d77,07fd11cb-c6b0-5ff0-83ae-70dcc2f87bc0,d2676c74-aadd-50e4-b763-0a533f11f3b3,1d75d4c9-e096-5935-9faf-031000a7a204,69b883f8-b30e-5df7-be9f-8c0fa52d4759,afde9309-39
52-5034-93e1-f2f191febf1b,06699f83-2eff-502c-a109-cc7fc41d51b8,91c33c37-7d16-5c70-8890-0c1a85ac92bb,36007962-1c44-502c-b8ae-9cc2cb63c306,78ee03df-b7c8-5511-9ecf-458fb12b500e,93b17d4e-0d3b-5a29-9d7c-6f3d98b04c9a,4bca2f8e-d024-5d0c-aa46-0e4e12aef53f,cb504c7c-97e4-5667-9ce3-3c6a95d7bc1c,32e40d97-c0a5-59e4-b422-5ca40b18517e,fdb9298b-2fe4-5611-9231-6d4ba7299808,8695ad82-3025-5bfe-9bf5-1f1085fc986a,5a60d944-5fe0-56fb-adcd-2d10032a8a52,c6263e62-751f-57c3-9b6f-25395621464b,fc733e9f-9716-5931-a501-a76d1add9d9d,bb858e61-762
4-54a2-bcea-1c6f8cb878b8,1c57c45a-4a1c-5eb3-94b9-38a947ebde3d,c73afc40-8557-5362-bed7-d6df854876c2,bc24ff23-cdcc-5f81-9f34-86e6d44cabb5,c58b859e-835e-57ce-aecb-5da6c890b12a,09681adf-5088-5bbd-9e18-52fde00d28f2,a0dded80-58c1-5462-836b-5d1eb4ca2ad1,c02747ac-2852-5e22-92db-49f7fc73a791,1986ede1-d777-57b7-a521-6d9befe42f72,882a7022-fcd4-5ceb-a20d-8c179f4c432b,00bde984-aa79-5138-8a18-17aa641f8652,5669ccb3-5ef6-5f43-8b13-98b2fce87370,19657f32-1cc8-5c9d-adbb-4329113d980c,773aa233-542e-57fa-82c0-45c33e713991,1f010d90-8d40
-5174-a13a-1edf3b1c65df,9c84a33a-d5ff-5e86-a058-5ab8125ca68e,d596b756-49d1-5cac-a309-7d638fa67335,43a821a1-8b68-581c-b6a4-e591af1e8fd7,7180b50b-45f9-5b04-81fa-fbca8c42d863,75649eb8-5df6-5d73-b9bd-a3d4d75a634f,d75f267c-a692-5103-99a4-7ee87ee2a20c,79f222e9-118d-56cc-8953-4da03fab5745,53887c9f-5efb-5cc9-b48d-b9b57a2ffd18,876e9371-3d13-5172-a48e-0cd8f94de368,20384379-7468-5076-9d3e-44f21030274c,534ddba2-a7dd-56e8-8ed4-fe924e15fd63,9faacec4-c59c-570e-af72-6c17492fd7d2,a5a7cce3-5a66-5f19-a961-868f301e6082,24be03e8-1660-
57bf-b5fc-e24d42369cc3,95b517f9-feaf-52ee-9668-326e1839e470,f1bb1d2e-3be1-598b-979b-73e1722c451a,f85d95d6-2350-58c2-8819-318704cde775,584aa483-2a08-5987-9c25-423743a8f649,91879de7-53e1-5c22-979a-dffd4bdbd9cd,da00f830-032f-5384-a45f-cc85090e74a9,c3ae6c2c-4379-556c-b271-d99ca4aada59,11774a2b-06f3-5e3d-bf7d-1c6af9bad2e7,0bc9fa78-64cf-5837-96d7-5465b07b644a,3b6d87b6-1fed-5c3d-b248-6fd5f820cef3,d6435210-8b53-53a3-bf53-f75455f66571,f04b4c0c-8a3d-5688-bcb0-b1bc1c19cac7,9f694a0f-0b94-5b7b-82e5-a5d7fc815194,20c07a97-3667-5
b14-8692-5c1c54881a2b,1994d3b4-8bc6-58f0-8394-3192bc2a8ca9,597f20d7-9271-50c0-95ce-f9ebb1be9968,b9ced5b1-1158-5751-b3ad-fdb984e3d5da,95887ee6-99d3-580d-bbfd-338941cf9840,d8027fb7-c5ea-5c9e-b897-94048989fc47,34b40eda-a51d-51b6-95ab-2fac058836e4,6f0b4b5a-0564-5e44-aefb-f48a1c2be1be,56f71449-851d-5ef6-8609-cee7a57612a9,1ad1553f-ef5c-5eef-95f3-0e605625ec6a,60bd91ef-4917-5832-816b-29d0605ccec9,d9bd3f94-69d0-5168-80de-aece0ae67768,25520f4f-4d33-5f0d-9bd0-f63b8dcd6332,7f51820f-009a-5e90-9472-e79cfca8ff57".

А это плагин

// gatsby-node.js

const chalk = require('chalk')
const axios = require('axios')
const { createRemoteFileNode } = require(`gatsby-source-filesystem`)

/**
 * ============================================================================
 * Helper functions and constants
 * ============================================================================
 */
const REVIEW_NODE_TYPE = 'StampedProductReview'
const REVIEW_NODE_IMAGE_TYPE = 'StampedProductReviewImage'
const STAMPED_IMG_URL_PREFIX = 'https://s3-us-west-2.amazonaws.com/stamped.io/uploads/photos'

function encode(type, id, params = {}) {
  let full = `gid://shopify/${type}/${id}`
  let query = []
  const keys = Object.keys(params)
  if (keys.length > 0) {
    for (let i = 0; i < keys.length; i++) {
      query.push(keys[i] + '=' + params[keys[i]])
    }
    query = '?' + query.join('&')
    full += query
  }
  return typeof window === 'undefined' ? Buffer.from(full, 'utf-8').toString('base64') : btoa(full) // eslint-disable-line no-undef
}

/**
 * ============================================================================
 * Verify plugin loads
 * ============================================================================
 */
exports.onPreInit = () => console.log(chalk.magentaBright('Loaded gatsby-shopify-stamped-reviews'))

/**
 * ============================================================================
 * Link nodes together with a customized GraphQL Schema
 * ============================================================================
 */
exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  createTypes(`
    type StampedProductReview implements Node {
      id: ID!
      slug: String!
      description: String!
      author: String
      reviewTitle: String
      reviewMessage: String
      reviewRating: Int
      reviewDate: Date
      reviewUserPhotos: String
      reviewUserVideos: String
      reviewVerifiedType: Int
      reviewReply: String
      reviewReplyDate: Date
      productId: String
      productName: String
      productSKU: String
      productUrl: String
      productImageUrl: String
      productImageLargeUrl: String
      productImageThumbnailUrl: String
      productDescription: String
      avatar: String
      location: String
      reviewVotesUp: Int
      reviewVotesDown: Int
      userReference: String
      dateCreated: Date
      dateReplied: Date
      reviewType: Int
      widgetType: String
      shopifyProductId: String
      product: ShopifyProduct @link(by: "shopifyId", from: "shopifyProductId")
      images: [StampedProductReviewImage]
    }
    type StampedProductReviewImage {
      id: String
      altText: String
      originalSrc: String
      localFile: File @link(by: "id", from: "localFile___NODE")
    }
    type ShopifyProduct implements Node {
      reviews: [StampedProductReview] @link(by: "shopifyProductId", from: "storefrontId")
    }
  `)
}

/**
 * ============================================================================
 * Source and cache nodes from the API
 * ============================================================================
 */
exports.sourceNodes = async ({ actions, createContentDigest, createNodeId }, pluginOptions) => {
  // Constants & Variables
  const STORE_URL = `${pluginOptions.storeUrl}.myshopify.com`
  const STAMPED_PUBLIC_KEY = pluginOptions.apiKeyPublic
  const REVIEWS_PER_PAGE = 100 // Maximum is 100
  const ONLY_WITH_PHOTOS = false
  const MIN_RATING = 0
  const SORT_REVIEWS = 'featured'
  let reviews = []
  let totalNumberOfReviews
  let totalNumberOfPages = null
  let currentPage = 1
  const stampedUrl = (pageNumber) =>
    `https://stamped.io/api/widget/reviews?minRating=${MIN_RATING}&take=${REVIEWS_PER_PAGE}&page=${pageNumber}&isWithPhotos=${ONLY_WITH_PHOTOS}&storeUrl=${STORE_URL}&apiKey=${STAMPED_PUBLIC_KEY}`

  const { createNode } = actions

  const getReviews = (pageNumber) => {
    return axios(stampedUrl(pageNumber), {
      method: 'get',
      headers: {},
    })
      .then((response) => {
        if (totalNumberOfReviews !== response.data.total) {
          totalNumberOfReviews = response.data.total
        }
        if (totalNumberOfPages === null) {
          totalNumberOfPages = Math.ceil(totalNumberOfReviews / REVIEWS_PER_PAGE)
        }
        return response.data
      })
      .catch((error) => {
        console.log(chalk.magentaBright('Stamped request error', error.message))
        throw new Error(error.message)
      })
  }

  // Grab first page of reviews, or all reviews if there's only one page
  console.log(chalk.magentaBright('Gathering all Stamped.io Reviews'))
  const data = await getReviews(currentPage)
  reviews = [...reviews, ...data.data]

  // If additional queries are needed (number of reviews is greater than REVIEWS_PER_PAGE -> max: 100) gather queries into an array of queries
  let additionalPageQueries = []
  while (currentPage < totalNumberOfPages) {
    currentPage += 1
    additionalPageQueries.push(getReviews(currentPage).then((data) => data.data))
  }

  // Run all additional queries together asynchronously and combine with first query
  const additionalReponses = await Promise.all(additionalPageQueries)
  reviews =
    additionalReponses.flat().length > 0 ? [...reviews, ...additionalReponses.flat()] : reviews

  console.log(chalk.magentaBright('Done!'))

  // loop through data and create Gatsby nodes
  reviews.forEach((review) => {
    let images = []
    let imageUrls = []
    if (review.reviewUserPhotos && review.reviewUserPhotos !== '') {
      imageUrls = review.reviewUserPhotos.split(',')
    }
    imageUrls.forEach((imageUrl, i) => {
      images.push({
        id: createNodeId(`${REVIEW_NODE_IMAGE_TYPE}-${i}`),
        parent: review,
        children: [],
        internal: {
          type: REVIEW_NODE_IMAGE_TYPE,
          content: JSON.stringify(imageUrl),
          contentDigest: createContentDigest(imageUrl),
        },
        altText: `${review.author}'s product review photo #${i}`,
        originalSrc: `${STAMPED_IMG_URL_PREFIX}/${imageUrl}`,
      })
    })

    createNode({
      ...review,
      images,
      id: createNodeId(`${REVIEW_NODE_TYPE}-${review.id}`),
      parent: null,
      children: [],
      internal: {
        type: REVIEW_NODE_TYPE,
        content: JSON.stringify(review),
        contentDigest: createContentDigest(review),
      },
      shopifyProductId: encode('Product', review.productId),
    })
  })

  return
}

/**
 * ============================================================================
 * Transform remote file nodes
 * ============================================================================
 */
exports.onCreateNode = async ({
  node,
  actions: { createNode },
  cache,
  createNodeId,
  getCache,
  store,
}) => {
  if (node.internal.type === REVIEW_NODE_TYPE && node.images.length > 0) {
    for (const image of node.images) {
      let fileNode = await createRemoteFileNode({
        url: image.originalSrc,
        parentNodeId: image.id,
        createNode,
        createNodeId,
        getCache,
        store,
      })

      if (fileNode) {
        image.localFile___NODE = fileNode.id
      }
    }
  }
}


person Kevmon    schedule 12.06.2021    source источник