",
+ "license": "MIT",
"bugs": {
"url": "https://github.com/gatsbyjs/gatsby/issues"
},
- "keywords": [
- "gatsby",
- "gatsby-plugin",
- "gatsby-source-plugin",
- "shopify"
- ],
- "author": "Angelo Ashmore",
- "license": "MIT",
- "peerDependencies": {
- "gatsby": "^3.0.0-next.0"
- },
- "devDependencies": {
- "@babel/cli": "^7.14.0",
- "@babel/core": "^7.14.0",
- "cross-env": "^7.0.3"
- },
+ "homepage": "https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-shopify#readme",
"dependencies": {
- "babel-preset-gatsby-package": "^1.8.0-next.1",
- "chalk": "^4.1.0",
- "gatsby-node-helpers": "^0.3.0",
- "gatsby-source-filesystem": "^3.8.0-next.1",
- "graphql-request": "^1.8.2",
- "lodash": "^4.17.21",
- "p-iteration": "^1.1.8",
- "prettyjson": "^1.2.1"
+ "gatsby-core-utils": "^2.8.0-next.0",
+ "gatsby-source-filesystem": "^3.8.0-next.0",
+ "node-fetch": "^2.6.1",
+ "sharp": "^0.28.0",
+ "shift-left": "^0.1.5"
},
- "engines": {
- "node": ">=12.13.0"
+ "devDependencies": {
+ "@types/jest": "^26.0.20",
+ "@types/node": "^14.14.34",
+ "@types/node-fetch": "^2.5.8",
+ "@types/sharp": "^0.28.0",
+ "gatsby-plugin-image": "^1.8.0-next.0",
+ "msw": "^0.27.1",
+ "prettier": "^2.2.1",
+ "prettier-check": "^2.0.0",
+ "tsc-watch": "^4.2.9",
+ "typescript": "^4.2.3"
},
- "main": "index.js"
+ "peerDependencies": {
+ "gatsby-plugin-image": "^1.1.0"
+ }
}
diff --git a/packages/gatsby-source-shopify/src/__tests__/__snapshots__/index.js.snap b/packages/gatsby-source-shopify/src/__tests__/__snapshots__/index.js.snap
deleted file mode 100644
index fa5517a35f3ad..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/__snapshots__/index.js.snap
+++ /dev/null
@@ -1,3083 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`gatsby-source-shopify Generates nodes 1`] = `
-Object {
- "Shopify__Blog__Z2lkOi8vc2hvcGlmeS9CbG9nLzEyNzIzNDg2OTc=": Object {
- "children": Array [],
- "id": "Shopify__Blog__Z2lkOi8vc2hvcGlmeS9CbG9nLzEyNzIzNDg2OTc=",
- "internal": Object {
- "contentDigest": "b796c451cadc7db2c68166806ef0a774",
- "type": "ShopifyBlog",
- },
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9CbG9nLzEyNzIzNDg2OTc=",
- "title": "News",
- "url": "https://gatsby-swag.myshopify.com/blogs/news",
- },
- "Shopify__Collection__Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzMzNzc1NzQ3MTYw": Object {
- "children": Array [],
- "description": "The items in this collection are offered to contributors for free as a token of our appreciate for contributing to Gatsby.",
- "descriptionHtml": "The items in this collection are offered to contributors for free as a token of our appreciate for contributing to Gatsby.",
- "handle": "contributor-swag",
- "id": "Shopify__Collection__Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzMzNzc1NzQ3MTYw",
- "image": null,
- "internal": Object {
- "contentDigest": "26722d264ec8614eee54f73815a1a509",
- "type": "ShopifyCollection",
- },
- "parent": "__SOURCE__",
- "products___NODE": Array [
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NzAyMjQ5Nzg4MA==",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTU2NTI0NDA=",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTUyNTkyMjQ=",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzM4MTkwNDIxNzcxMTI=",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwNTg0MDQxNjc3Njg=",
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzMzNzc1NzQ3MTYw",
- "title": "Contributor Swag",
- "updatedAt": "2019-08-09T07:35:01Z",
- },
- "Shopify__Collection__Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzcwNTQwMDAxNTM=": Object {
- "children": Array [],
- "description": "",
- "descriptionHtml": "",
- "handle": "frontpage",
- "id": "Shopify__Collection__Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzcwNTQwMDAxNTM=",
- "image": null,
- "internal": Object {
- "contentDigest": "edd8cca27a08ef6a2e844638aa6e8db5",
- "type": "ShopifyCollection",
- },
- "parent": "__SOURCE__",
- "products___NODE": Array [
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEzMTIxMjA1Njk5NDQ=",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NzAyMjQ5Nzg4MA==",
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzcwNTQwMDAxNTM=",
- "title": "Home page",
- "updatedAt": "2019-08-09T07:35:01Z",
- },
- "Shopify__Collection__Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzg4MjE2NzY0NTA0": Object {
- "children": Array [],
- "description": "",
- "descriptionHtml": "",
- "handle": "level-2",
- "id": "Shopify__Collection__Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzg4MjE2NzY0NTA0",
- "image": null,
- "internal": Object {
- "contentDigest": "2f9de13f7b583e455579c10d1a26c2e9",
- "type": "ShopifyCollection",
- },
- "parent": "__SOURCE__",
- "products___NODE": Array [
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=",
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MjM0MTkzNDA4ODg=",
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzg4MjE2NzY0NTA0",
- "title": "Level 2",
- "updatedAt": "2019-08-08T16:05:04Z",
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE3NTY2ODU1MDA1MDQ=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE3NTY2ODU1MDA1MDQ=",
- "internal": Object {
- "contentDigest": "4d8c66df4e7da219ce29b45d2051fad1",
- "type": "ShopifyProductOption",
- },
- "name": "Title",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE3NTY2ODU1MDA1MDQ=",
- "values": Array [
- "Default Title",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODI3MDcyODg=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODI3MDcyODg=",
- "internal": Object {
- "contentDigest": "2e9069356a7e121c17168852f34a24e0",
- "type": "ShopifyProductOption",
- },
- "name": "Title",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODI3MDcyODg=",
- "values": Array [
- "Default Title",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODMyNjQzNDQ=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODMyNjQzNDQ=",
- "internal": Object {
- "contentDigest": "0818971c1f5532b744e30bc94cd79fa9",
- "type": "ShopifyProductOption",
- },
- "name": "Title",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODMyNjQzNDQ=",
- "values": Array [
- "Default Title",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNDY4ODA2Njc3MzY=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNDY4ODA2Njc3MzY=",
- "internal": Object {
- "contentDigest": "3ff8a67ed3695804a8d2577d2a6db2f7",
- "type": "ShopifyProductOption",
- },
- "name": "Size",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNDY4ODA2Njc3MzY=",
- "values": Array [
- "SM — Women",
- "MD - Women",
- "LG — Women",
- "XL - Women",
- "SM — Unisex",
- "MD - Unisex",
- "LG — Unisex",
- "XL — Unisex",
- "2XL — Unisex",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNTAwNjU2ODQ1Njg=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNTAwNjU2ODQ1Njg=",
- "internal": Object {
- "contentDigest": "90568d8f206a4614bd438239597a3489",
- "type": "ShopifyProductOption",
- },
- "name": "Size",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNTAwNjU2ODQ1Njg=",
- "values": Array [
- "One size fits most",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI2OTk3ODgzODYzOTI=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI2OTk3ODgzODYzOTI=",
- "internal": Object {
- "contentDigest": "c2e66d7f53c9456a2f5625ad29afeae1",
- "type": "ShopifyProductOption",
- },
- "name": "Title",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI2OTk3ODgzODYzOTI=",
- "values": Array [
- "Default Title",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjg2ODMwOTY=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjg2ODMwOTY=",
- "internal": Object {
- "contentDigest": "3a4835fa4ae571f43550cdd98a7f62ab",
- "type": "ShopifyProductOption",
- },
- "name": "Size",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjg2ODMwOTY=",
- "values": Array [
- "XS - Unisex",
- "SM - Unisex",
- "MD - Unisex",
- "L - Unisex",
- "XL - Unisex",
- "2XL - Unisex",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjk2MzMzNjg=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjk2MzMzNjg=",
- "internal": Object {
- "contentDigest": "b8195058dbbf0e598fb855afefc887ae",
- "type": "ShopifyProductOption",
- },
- "name": "Size",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjk2MzMzNjg=",
- "values": Array [
- "XS - Unisex",
- "SM - Unisex",
- "MD - Unisex",
- "L - Unisex",
- "XL - Unisex",
- "2XL - Unisex",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3NzExMDcwODY0MjQ=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3NzExMDcwODY0MjQ=",
- "internal": Object {
- "contentDigest": "da26fd3cb460052d35cb8321a34d4df2",
- "type": "ShopifyProductOption",
- },
- "name": "Size",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3NzExMDcwODY0MjQ=",
- "values": Array [
- "XS - Unisex",
- "S - Unisex",
- "M - Unisex",
- "L - Unisex",
- "XL - Unisex",
- "2XL - Unisex",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3OTYxNTk3NjI1MjA=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3OTYxNTk3NjI1MjA=",
- "internal": Object {
- "contentDigest": "75c0d5cbea3abf149d39cbbaadc9497c",
- "type": "ShopifyProductOption",
- },
- "name": "Size",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3OTYxNTk3NjI1MjA=",
- "values": Array [
- "S - Men",
- "MD - Men",
- "LG - Men",
- "XL - Men",
- "2XL - Men",
- "3XL - Men",
- "S - Womens",
- "MD - Womens",
- "LG - Womens",
- "XL - Womens",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4NTY2MDg5ODkyNzI=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4NTY2MDg5ODkyNzI=",
- "internal": Object {
- "contentDigest": "38bb3f25c9d97a273cf437717cdd725b",
- "type": "ShopifyProductOption",
- },
- "name": "Size",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4NTY2MDg5ODkyNzI=",
- "values": Array [
- "X-Small",
- "Small",
- "Medium",
- "Large",
- "X-Large",
- "XX-Large",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4ODQ2NjU2NzE3Njg=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4ODQ2NjU2NzE3Njg=",
- "internal": Object {
- "contentDigest": "985a9913d4cda3f1e32cb352cd2efe55",
- "type": "ShopifyProductOption",
- },
- "name": "Title",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4ODQ2NjU2NzE3Njg=",
- "values": Array [
- "Default Title",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzIxMzUwODQ1NjQ1Njg=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzIxMzUwODQ1NjQ1Njg=",
- "internal": Object {
- "contentDigest": "aeb018e8694217e289c7591351f67fba",
- "type": "ShopifyProductOption",
- },
- "name": "Title",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzIxMzUwODQ1NjQ1Njg=",
- "values": Array [
- "Default Title",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzMwNTEwMTk5NjA1Nw==": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzMwNTEwMTk5NjA1Nw==",
- "internal": Object {
- "contentDigest": "b50652dfd63bcb98c4ab73149d72246f",
- "type": "ShopifyProductOption",
- },
- "name": "Size",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzMwNTEwMTk5NjA1Nw==",
- "values": Array [
- "SM — Women",
- "MD — Women",
- "LG — Women",
- "XL - Women",
- "SM - Unisex",
- "MD - Unisex",
- "LG — Unisex",
- "XL — Unisex",
- "2XL — Unisex",
- ],
- },
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzQ5OTgzMjAzMjQ2OTY=": Object {
- "children": Array [],
- "id": "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzQ5OTgzMjAzMjQ2OTY=",
- "internal": Object {
- "contentDigest": "f987805fb7889d847d4b96f366b93e9a",
- "type": "ShopifyProductOption",
- },
- "name": "Title",
- "parent": "__SOURCE__",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzQ5OTgzMjAzMjQ2OTY=",
- "values": Array [
- "Default Title",
- ],
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjA5MTc2": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjA5MTc2",
- "image": Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- "internal": Object {
- "contentDigest": "a3ee401ff2a8db5db5031f54f5e8820d",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "SM — Women",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjA5MTc2",
- "sku": "PT-S-W",
- "title": "SM — Women",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjQxOTQ0": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjQxOTQ0",
- "image": Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- "internal": Object {
- "contentDigest": "35c6299896a00cfff7d7c18dac0f4eb4",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "LG — Women",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjQxOTQ0",
- "sku": "PT-LG-W",
- "title": "LG — Women",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzA3NDgw": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzA3NDgw",
- "image": Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- "internal": Object {
- "contentDigest": "b95cc0d09fc67973b61bf6d87bf875e0",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "SM — Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzA3NDgw",
- "sku": "PT-SM-M",
- "title": "SM — Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzczMDE2": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzczMDE2",
- "image": Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- "internal": Object {
- "contentDigest": "3d0967c9e5fd64fc9baf31a0273b70fd",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "LG — Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzczMDE2",
- "sku": "PT-LG-M",
- "title": "LG — Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzNDA1Nzg0": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzNDA1Nzg0",
- "image": Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- "internal": Object {
- "contentDigest": "02dbf1b3c90354eca2dd4295192a4c36",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XL — Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzNDA1Nzg0",
- "sku": "PT-XL-M",
- "title": "XL — Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjAwMjk0NzYwNTM2": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjAwMjk0NzYwNTM2",
- "image": Object {
- "altText": "Mockup of the Gatsby socks.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIzMjYyNTI0MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/sock-mockup_1.jpg?v=1531343345",
- },
- "internal": Object {
- "contentDigest": "b70e12f3f05b5b8b14e26652cb0c5c51",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NzAyMjQ5Nzg4MA==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "One size fits most",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjAwMjk0NzYwNTM2",
- "sku": "S-ONE",
- "title": "One size fits most",
- "weight": 3,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzI0OTA0MDI0": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzI0OTA0MDI0",
- "image": Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- "internal": Object {
- "contentDigest": "0016aad4c9786bf6fd6ffcd6702bcc0a",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "2XL — Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzI0OTA0MDI0",
- "sku": "PT-2XL-M",
- "title": "2XL — Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzIwODQwNzky": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzIwODQwNzky",
- "image": Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- "internal": Object {
- "contentDigest": "b7ac2da5e2775c93253624c67a9ce9be",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "2XL — Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzIwODQwNzky",
- "sku": "DDT-2XL-M",
- "title": "2XL — Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjIzOTc3Mjg3Njg4OA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjIzOTc3Mjg3Njg4OA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDYzMjM1Mjg1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6208.jpg?v=1555548544",
- },
- "internal": Object {
- "contentDigest": "d3e975028946f23934ff851a76921704",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "2.00",
- "priceNumber": 2,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEzMTIxMjA1Njk5NDQ=",
- "selectedOptions": Array [
- Object {
- "name": "Title",
- "value": "Default Title",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjIzOTc3Mjg3Njg4OA==",
- "sku": "STICKERS-01",
- "title": "Default Title",
- "weight": 1,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjM3NjMyMTUyMzgwMA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjM3NjMyMTUyMzgwMA==",
- "image": Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- "internal": Object {
- "contentDigest": "edd0bd9b9f486cc781aa1081a392377f",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "LG — Women",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjM3NjMyMTUyMzgwMA==",
- "sku": "DDT-LG-W",
- "title": "LG — Women",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjQ0NTY4NzcwOTc4NA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjQ0NTY4NzcwOTc4NA==",
- "image": Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- "internal": Object {
- "contentDigest": "dd04923fdfccea530b7720c411168318",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "MD - Women",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjQ0NTY4NzcwOTc4NA==",
- "sku": "PT-M-W",
- "title": "MD - Women",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0MjA4ODc5Mg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0MjA4ODc5Mg==",
- "image": Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- "internal": Object {
- "contentDigest": "da5ec3103887a13262573985eaee2010",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "MD - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0MjA4ODc5Mg==",
- "sku": "DDT-MD-M",
- "title": "MD - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0NTg1NzExMg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0NTg1NzExMg==",
- "image": Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- "internal": Object {
- "contentDigest": "b49fd6f96b04c56a0ec8576d128c7590",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "SM - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0NTg1NzExMg==",
- "sku": "DDT-SM-M",
- "title": "SM - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDMyNjUyMzk5Mg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDMyNjUyMzk5Mg==",
- "image": Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- "internal": Object {
- "contentDigest": "a48eb1e29980030eba1796f178735e13",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "MD - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDMyNjUyMzk5Mg==",
- "sku": "PT-MD-M",
- "title": "MD - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2MTYzMzExMg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2MTYzMzExMg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5NzEzNTQ0OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4839_002.jpg?v=1546015349",
- },
- "internal": Object {
- "contentDigest": "031a0c7d6c7a4cd61b80c29149f95c1c",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTUyNTkyMjQ=",
- "selectedOptions": Array [
- Object {
- "name": "Title",
- "value": "Default Title",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2MTYzMzExMg==",
- "sku": "1756S",
- "title": "Default Title",
- "weight": 4,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2NTIwNDgyNA==": Object {
- "availableForSale": false,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2NTIwNDgyNA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5ODA4NTcyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4862.jpg?v=1540860991",
- },
- "internal": Object {
- "contentDigest": "1e3fbc328de15d4703534a3692e33e08",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTU2NTI0NDA=",
- "selectedOptions": Array [
- Object {
- "name": "Title",
- "value": "Default Title",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2NTIwNDgyNA==",
- "sku": "BA540",
- "title": "Default Title",
- "weight": 4,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xNTM5ODYzOTc5NjMxMg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xNTM5ODYzOTc5NjMxMg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNDQ5ODk5NTI4MjAwOA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4947.png?v=1542402545",
- },
- "internal": Object {
- "contentDigest": "ff82d5ccc2c81914ae081db2e2e8e096",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "2.00",
- "priceNumber": 2,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE1NjYyNjk5OTcxNDQ=",
- "selectedOptions": Array [
- Object {
- "name": "Title",
- "value": "Default Title",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xNTM5ODYzOTc5NjMxMg==",
- "sku": "lapelpin",
- "title": "Default Title",
- "weight": 0.5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTA2ODQx": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTA2ODQx",
- "image": Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- "internal": Object {
- "contentDigest": "5d026c59b31e1afb5aa1502e41f4a97f",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "SM — Women",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTA2ODQx",
- "sku": "DDT-SM-W",
- "title": "SM — Women",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTM5NjA5": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTM5NjA5",
- "image": Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- "internal": Object {
- "contentDigest": "c28be7fcd6d7fbdc6eb4eae7a5112fc7",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "MD — Women",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTM5NjA5",
- "sku": "DDT-MD-W",
- "title": "MD — Women",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MjcwNjgx": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MjcwNjgx",
- "image": Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- "internal": Object {
- "contentDigest": "676cfb826c6503f9fd7f88b49f5e2384",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "LG — Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MjcwNjgx",
- "sku": "DDT-LG-M",
- "title": "LG — Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MzAzNDQ5": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MzAzNDQ5",
- "image": Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- "internal": Object {
- "contentDigest": "90774ef31230bc817339b937734211a4",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XL — Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MzAzNDQ5",
- "sku": "DDT-XL-M",
- "title": "XL — Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTQ1MjAwMDczMTIyNA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTQ1MjAwMDczMTIyNA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjYwMTc4ODM5MTUxMg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG-5659.png?v=1543972353",
- },
- "internal": Object {
- "contentDigest": "6e40fde47dd34c9b42a7d5cebd02abae",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MjM0MTkzNDA4ODg=",
- "selectedOptions": Array [
- Object {
- "name": "Title",
- "value": "Default Title",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTQ1MjAwMDczMTIyNA==",
- "sku": "V-12-00002",
- "title": "Default Title",
- "weight": 6,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTg5Mjk1Mg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTg5Mjk1Mg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613",
- },
- "internal": Object {
- "contentDigest": "54cc5d9a012455ac0c8b5d12cb8711ac",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XS - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTg5Mjk1Mg==",
- "sku": "FLP-1",
- "title": "XS - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk1ODQ4OA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk1ODQ4OA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613",
- },
- "internal": Object {
- "contentDigest": "9603ce48113f4428eaa0fdc4770b9f7f",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "MD - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk1ODQ4OA==",
- "sku": "FLP-3",
- "title": "MD - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk5MTI1Ng==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk5MTI1Ng==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613",
- },
- "internal": Object {
- "contentDigest": "32a38d46cea7aedb829954bffabc9f1b",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "L - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk5MTI1Ng==",
- "sku": "FLP-4",
- "title": "L - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTkyNTcyMA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTkyNTcyMA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613",
- },
- "internal": Object {
- "contentDigest": "bdd32030543110e4e2e2235f202e6901",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "SM - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTkyNTcyMA==",
- "sku": "FLP-2",
- "title": "SM - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjA1Njc5Mg==": Object {
- "availableForSale": false,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjA1Njc5Mg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613",
- },
- "internal": Object {
- "contentDigest": "ced8908979cee42693fdeeae897041c3",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "2XL - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjA1Njc5Mg==",
- "sku": "FLP-6",
- "title": "2XL - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjAyNDAyNA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjAyNDAyNA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613",
- },
- "internal": Object {
- "contentDigest": "e12cd1858f654a895284634f99aaafeb",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XL - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjAyNDAyNA==",
- "sku": "FLP-5",
- "title": "XL - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzA3MjYwMA==": Object {
- "availableForSale": false,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzA3MjYwMA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022",
- },
- "internal": Object {
- "contentDigest": "e477def9619ab33fb88008895e1eeb61",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XS - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzA3MjYwMA==",
- "sku": "JSJP-1",
- "title": "XS - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzE3MDkwNA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzE3MDkwNA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022",
- },
- "internal": Object {
- "contentDigest": "0b72649db00228f90c39877713c0905f",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "L - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzE3MDkwNA==",
- "sku": "JSJP-4",
- "title": "L - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEwNTM2OA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEwNTM2OA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022",
- },
- "internal": Object {
- "contentDigest": "d6a31e8d02ef91df652d8c03c3d2217d",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "SM - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEwNTM2OA==",
- "sku": "JSJP-2",
- "title": "SM - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEzODEzNg==": Object {
- "availableForSale": false,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEzODEzNg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022",
- },
- "internal": Object {
- "contentDigest": "3101871722c3f260a3811b0739ee1496",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "MD - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEzODEzNg==",
- "sku": "JSJP-3",
- "title": "MD - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIwMzY3Mg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIwMzY3Mg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022",
- },
- "internal": Object {
- "contentDigest": "14d3577481332fee33171db68c549878",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XL - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIwMzY3Mg==",
- "sku": "JSJP-5",
- "title": "XL - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIzNjQ0MA==": Object {
- "availableForSale": false,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIzNjQ0MA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022",
- },
- "internal": Object {
- "contentDigest": "d8422f8b268010cda4c10374cebb1842",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "2XL - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIzNjQ0MA==",
- "sku": "JSJP-6",
- "title": "2XL - Unisex",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjQ3MzAwMTA0OA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjQ3MzAwMTA0OA==",
- "image": Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- "internal": Object {
- "contentDigest": "4fc1e1bdd9db19e5c9f60a31588e765e",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XL - Women",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjQ3MzAwMTA0OA==",
- "sku": "PT-XL-W",
- "title": "XL - Women",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjUyMjgwODQwOA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjUyMjgwODQwOA==",
- "image": Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- "internal": Object {
- "contentDigest": "f6a63b58c1ec66377cedfaed21a2e0a9",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XL - Women",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjUyMjgwODQwOA==",
- "sku": "DDT-XL-W",
- "title": "XL - Women",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MTc0MzcwNA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MTc0MzcwNA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115",
- },
- "internal": Object {
- "contentDigest": "4cae0b23be3894fa0a67cf66bbd3d857",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XS - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MTc0MzcwNA==",
- "sku": "AA9590XS",
- "title": "XS - Unisex",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MjU5NTY3Mg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MjU5NTY3Mg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115",
- },
- "internal": Object {
- "contentDigest": "185c427847c680988c7e4d9cbb3e809d",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "S - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MjU5NTY3Mg==",
- "sku": "AA9590S",
- "title": "S - Unisex",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NjM5Njc2MA==": Object {
- "availableForSale": false,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NjM5Njc2MA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115",
- },
- "internal": Object {
- "contentDigest": "9880bab23e7adc1552ebc2ac00f583fc",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "M - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NjM5Njc2MA==",
- "sku": "AA9590M",
- "title": "M - Unisex",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0Njc4OTk3Ng==": Object {
- "availableForSale": false,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0Njc4OTk3Ng==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115",
- },
- "internal": Object {
- "contentDigest": "2bdbefa4c461684d753d222c8bdc1976",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "L - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0Njc4OTk3Ng==",
- "sku": "AA9590L",
- "title": "L - Unisex",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NzI4MTQ5Ng==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NzI4MTQ5Ng==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115",
- },
- "internal": Object {
- "contentDigest": "d4bb4397a485a4cb5b2286e3d2a7d74e",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XL - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NzI4MTQ5Ng==",
- "sku": "AA9590XL",
- "title": "XL - Unisex",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0ODEzMzQ2NA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0ODEzMzQ2NA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115",
- },
- "internal": Object {
- "contentDigest": "64b0878287761c063d0a0101faf5a81e",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "2XL - Unisex",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0ODEzMzQ2NA==",
- "sku": "AA95902XL",
- "title": "2XL - Unisex",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTU5OTk2MA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTU5OTk2MA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "dfe073cf911dba2c1712dc4357093b0f",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "LG - Men",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTU5OTk2MA==",
- "sku": "TM98140",
- "title": "LG - Men",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTUzNDQyNA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTUzNDQyNA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "91e971a4a8a9a78a70142e0e76575028",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "MD - Men",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTUzNDQyNA==",
- "sku": "TM98139",
- "title": "MD - Men",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY2NTQ5Ng==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY2NTQ5Ng==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "1e1bd054b2976f40848b5fa7735a8e17",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XL - Men",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY2NTQ5Ng==",
- "sku": "TM98141",
- "title": "XL - Men",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY5ODI2NA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY5ODI2NA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "cf20f4368a61f4085aebb4951a86f759",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "2XL - Men",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY5ODI2NA==",
- "sku": "TM98142",
- "title": "2XL - Men",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTc2MzgwMA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTc2MzgwMA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "b46a8f0597e8da6f3c20ad08e7bf8089",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "3XL - Men",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTc2MzgwMA==",
- "sku": "TM98143",
- "title": "3XL - Men",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY0NTk4MzMyMA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY0NTk4MzMyMA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "48ea6af557034607b6536735c5bc985f",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "S - Womens",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY0NTk4MzMyMA==",
- "sku": "TM18135S",
- "title": "S - Womens",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY1ODUzMzQ2NA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY1ODUzMzQ2NA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "10cf7cbb14bd1531f1a6d1f2b124a524",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "MD - Womens",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY1ODUzMzQ2NA==",
- "sku": "TM18135M",
- "title": "MD - Womens",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY3MjQ1OTg2NA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY3MjQ1OTg2NA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "c0ffb0a2893859d36a70bb321e8db710",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "LG - Womens",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY3MjQ1OTg2NA==",
- "sku": "TM18135",
- "title": "LG - Womens",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY4MDU1MzU2MA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY4MDU1MzU2MA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "31a879ae7af658f6c0c4ce02d2c4506f",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XL - Womens",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY4MDU1MzU2MA==",
- "sku": "TM18135",
- "title": "XL - Womens",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0Mjc0NTA3Mzc1Mg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0Mjc0NTA3Mzc1Mg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- "internal": Object {
- "contentDigest": "b320d5f24acbd889c803704d9e4d9f8b",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "26.00",
- "priceNumber": 26,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "S - Men",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0Mjc0NTA3Mzc1Mg==",
- "sku": "TM18135S",
- "title": "S - Men",
- "weight": 7,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY0ODUzNg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY0ODUzNg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165",
- },
- "internal": Object {
- "contentDigest": "565d432180af8778827f0e68ecd7b342",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "Small",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY0ODUzNg==",
- "sku": "6735",
- "title": "Small",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY4MTMwNA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY4MTMwNA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165",
- },
- "internal": Object {
- "contentDigest": "ef2951de88dc0f2af2f703be493c8340",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "Medium",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY4MTMwNA==",
- "sku": "6736",
- "title": "Medium",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjYxNTc2OA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjYxNTc2OA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165",
- },
- "internal": Object {
- "contentDigest": "00c6060131140ad9a4d8be5bb8d97c78",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "X-Small",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjYxNTc2OA==",
- "sku": "6734",
- "title": "X-Small",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc0Njg0MA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc0Njg0MA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165",
- },
- "internal": Object {
- "contentDigest": "5d98703fae6648dd154e4a26ff7fc9ef",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "Large",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc0Njg0MA==",
- "sku": "6737",
- "title": "Large",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc3OTYwOA==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc3OTYwOA==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165",
- },
- "internal": Object {
- "contentDigest": "7572c259afaf6358fff3072d740eda92",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "X-Large",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc3OTYwOA==",
- "sku": "6738",
- "title": "X-Large",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjgxMjM3Ng==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjgxMjM3Ng==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165",
- },
- "internal": Object {
- "contentDigest": "942ff4f56f86aaed34eb0907c5a305cc",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=",
- "selectedOptions": Array [
- Object {
- "name": "Size",
- "value": "XX-Large",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjgxMjM3Ng==",
- "sku": "6739",
- "title": "XX-Large",
- "weight": 5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDMxNTMyMDA5MDcxMg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDMxNTMyMDA5MDcxMg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0Mzc1NjcyNjM2MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6192.jpg?v=1555695177",
- },
- "internal": Object {
- "contentDigest": "7a9842c6c7040f7fbd44f3f78e7881e1",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwNTg0MDQxNjc3Njg=",
- "selectedOptions": Array [
- Object {
- "name": "Title",
- "value": "Default Title",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDMxNTMyMDA5MDcxMg==",
- "sku": "SCNCH",
- "title": "Default Title",
- "weight": 0.5,
- "weightUnit": "OUNCES",
- },
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yOTAyOTE1MzQzOTgzMg==": Object {
- "availableForSale": true,
- "children": Array [],
- "compareAtPrice": null,
- "id": "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yOTAyOTE1MzQzOTgzMg==",
- "image": Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMTE2NzQ3MjcyMTkyODg=",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6689.jpg?v=1560443279",
- },
- "internal": Object {
- "contentDigest": "a97fbe8c6c26de50b281ab9738e6b627",
- "type": "ShopifyProductVariant",
- },
- "parent": "__SOURCE__",
- "price": "10.00",
- "priceNumber": 10,
- "product___NODE": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzM4MTkwNDIxNzcxMTI=",
- "selectedOptions": Array [
- Object {
- "name": "Title",
- "value": "Default Title",
- },
- ],
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yOTAyOTE1MzQzOTgzMg==",
- "sku": "jamstackhat",
- "title": "Default Title",
- "weight": 4,
- "weightUnit": "OUNCES",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTU2NTI0NDA=": Object {
- "availableForSale": false,
- "children": Array [],
- "createdAt": "2018-10-30T00:55:50Z",
- "description": "Dark grey and black, mesh, 6-panel trucker hat with snapback closure. Made of 100% polyester linen front panels and bill. Care Instructions: Spot clean with a damp cloth.",
- "descriptionHtml": "Dark grey and black, mesh, 6-panel trucker hat with snapback closure. Made of 100% polyester linen front panels and bill.
-Care Instructions: Spot clean with a damp cloth.
",
- "handle": "monogram-trucker-hat",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTU2NTI0NDA=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5ODA4NTcyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4862.jpg?v=1540860991",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5ODE1MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4855.jpg?v=1540861007",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5ODE4NDAyNA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4859_002.jpg?v=1540861027",
- },
- ],
- "internal": Object {
- "contentDigest": "819881f63ae1dcd3c5810ef75268fcb5",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODMyNjQzNDQ=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Hat",
- "publishedAt": "2018-10-30T00:55:50Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTU2NTI0NDA=",
- "tags": Array [],
- "title": "Monogram Trucker Hat",
- "updatedAt": "2019-08-05T22:55:26Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2NTIwNDgyNA==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTUyNTkyMjQ=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2018-10-30T00:52:49Z",
- "description": "Add more blazingly blazing speed to your wardrobe with this solid purple laundered chino twill hat. (Fine print: this is just a hat. It will not affect your speed.) Care Instructions: Spot clean with a damp cloth.",
- "descriptionHtml": "Add more blazingly blazing speed to your wardrobe with this solid purple laundered chino twill hat. (Fine print: this is just a hat. It will not affect your speed.)
-Care Instructions: Spot clean with a damp cloth.
",
- "handle": "blazing-purple-hat",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTUyNTkyMjQ=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5NzEzNTQ0OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4839_002.jpg?v=1546015349",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5NzMzMjA1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5147.jpg?v=1540860789",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5NzM5NzU5Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4848.jpg?v=1540860804",
- },
- ],
- "internal": Object {
- "contentDigest": "e3147e4ed64dd3d27402beb7d22da986",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODI3MDcyODg=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Hat",
- "publishedAt": "2018-10-30T00:52:49Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTUyNTkyMjQ=",
- "tags": Array [],
- "title": "This Purple Hat Is Blazing Fast",
- "updatedAt": "2019-08-07T17:39:01Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2MTYzMzExMg==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE1NjYyNjk5OTcxNDQ=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2018-11-16T19:36:04Z",
- "description": "Show us how you rock your custom Gatsby lapel pin! Silver plating, clois-tech lapel pin with a clutch attachment. Size – 0.835” Care Instructions: Please do not machine wash.",
- "descriptionHtml": "Show us how you rock your custom Gatsby lapel pin! Silver plating, clois-tech lapel pin with a clutch attachment. Size – 0.835”
-Care Instructions: Please do not machine wash.
",
- "handle": "gatsby-lapel-pin",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE1NjYyNjk5OTcxNDQ=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNDQ5ODk5NTI4MjAwOA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4947.png?v=1542402545",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNDQ5ODYzNTQ1NjYwMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4972.jpg?v=1542402545",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNDQ5ODYzNjgwMDA4OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4956.jpg?v=1542402545",
- },
- ],
- "internal": Object {
- "contentDigest": "2d0002a3d40b64e5d81dc86f9e9aee47",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzIxMzUwODQ1NjQ1Njg=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "2.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "2.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Pin",
- "publishedAt": "2018-11-16T19:36:04Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE1NjYyNjk5OTcxNDQ=",
- "tags": Array [],
- "title": "Gatsby Lapel Pin",
- "updatedAt": "2019-08-07T13:10:10Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xNTM5ODYzOTc5NjMxMg==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MjM0MTkzNDA4ODg=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2018-12-05T01:12:21Z",
- "description": "Keep your hot beverages BLAZING hot for hours with this stainless vacuum-insulated Fifty/Fifty 12 oz/345mL bottle. Also works to keep cold beverages, er, blazing cold? Care Instructions: Do not put in microwave, freezer, or dishwasher. Hand wash with hot soapy water. Leave cap off and allow to air dry. Do not use cleaners containing bleach or chlorine.",
- "descriptionHtml": "Keep your hot beverages BLAZING hot for hours with this stainless vacuum-insulated Fifty/Fifty 12 oz/345mL bottle. Also works to keep cold beverages, er, blazing cold?
-Care Instructions: Do not put in microwave, freezer, or dishwasher. Hand wash with hot soapy water. Leave cap off and allow to air dry. Do not use cleaners containing bleach or chlorine.
",
- "handle": "12oz-travel-mug",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MjM0MTkzNDA4ODg=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjYwMTc4ODM5MTUxMg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG-5659.png?v=1543972353",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjYwMTc4ODQ1NzA0OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG-5667.JPG?v=1543972362",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjYwMTc4ODU1NTM1Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG-5664.JPG?v=1543972368",
- },
- ],
- "internal": Object {
- "contentDigest": "332cd4baa31218f8ec5e9c7088fc5138",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI2OTk3ODgzODYzOTI=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Water Bottle",
- "publishedAt": "2018-12-05T01:12:21Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MjM0MTkzNDA4ODg=",
- "tags": Array [],
- "title": "Mug for (Blazing) Hot Beverages",
- "updatedAt": "2019-08-06T11:22:51Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTQ1MjAwMDczMTIyNA==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2018-12-28T22:36:24Z",
- "description": "For the work-from-home professional, Gatsby's Freelance Pants take “business casual” to a whole new level. These slick black pants will help you stay calm and reach #maximumcomf even when you're under pressure.",
- "descriptionHtml": "For the work-from-home professional, Gatsby's Freelance Pants take “business casual” to a whole new level. These slick black pants will help you stay calm and reach #maximumcomf even when you're under pressure.",
- "handle": "freelance-pants",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613",
- },
- ],
- "internal": Object {
- "contentDigest": "e0714d0bd6c30f33792f3682cd9be41d",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjg2ODMwOTY=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Pajama Pants",
- "publishedAt": "2018-12-28T22:36:24Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=",
- "tags": Array [],
- "title": "Freelance Pants",
- "updatedAt": "2019-08-07T07:53:05Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTg5Mjk1Mg==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTkyNTcyMA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk1ODQ4OA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk5MTI1Ng==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjAyNDAyNA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjA1Njc5Mg==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2018-12-28T22:43:00Z",
- "description": "Be the life of your own comfy party with these purple and playful JAMstack Jammies. Did we mention they have pockets?",
- "descriptionHtml": "Be the life of your own comfy party with these purple and playful JAMstack Jammies. Did we mention they have pockets?",
- "handle": "jamstack-jammies",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022",
- },
- ],
- "internal": Object {
- "contentDigest": "c7a8763c58abee73f3a7615d9e5e0dec",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjk2MzMzNjg=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Pajama Pants",
- "publishedAt": "2018-12-28T22:43:00Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=",
- "tags": Array [],
- "title": "JAMstack Jammies",
- "updatedAt": "2019-08-08T16:03:30Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzA3MjYwMA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEwNTM2OA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEzODEzNg==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzE3MDkwNA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIwMzY3Mg==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIzNjQ0MA==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2019-03-01T16:53:32Z",
- "description": "This fleece zip hoodie is perfect for keeping cozy! Featured with drawcords and split kangaroo pockets. Material: 52/48 Airlume combed and ringspun cotton/poly fleece. Care Instructions: Machine wash cold, tumble dry low.",
- "descriptionHtml": "This fleece zip hoodie is perfect for keeping cozy! Featured with drawcords and split kangaroo pockets. Material: 52/48 Airlume combed and ringspun cotton/poly fleece.
-Care Instructions: Machine wash cold, tumble dry low.
",
- "handle": "dark-monogram-hoodie",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115",
- },
- ],
- "internal": Object {
- "contentDigest": "1d17cea33a8247b0c6e1136bcae34396",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3NzExMDcwODY0MjQ=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Sweatshirt",
- "publishedAt": "2019-03-01T16:53:32Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=",
- "tags": Array [],
- "title": "Dark Monogram Hoodie",
- "updatedAt": "2019-08-07T18:18:24Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MTc0MzcwNA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MjU5NTY3Mg==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NjM5Njc2MA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0Njc4OTk3Ng==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NzI4MTQ5Ng==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0ODEzMzQ2NA==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2019-03-13T23:30:56Z",
- "description": "Say “no” to boring colors and go all purple everything with this comfy cotton-poly blend hoodie. Rib knit cuff with thumb exits, a rib knit hem, an interior phone pocket, and headphone cord port. Care Instructions: Super hot water and high dryer heat may shrink the sweatshirt due to the cotton content. Suggested to turn inside out while washing to protect the wear of the printed logo.",
- "descriptionHtml": "Say “no” to boring colors and go all purple everything with this comfy cotton-poly blend hoodie. Rib knit cuff with thumb exits, a rib knit hem, an interior phone pocket, and headphone cord port.
-Care Instructions: Super hot water and high dryer heat may shrink the sweatshirt due to the cotton content. Suggested to turn inside out while washing to protect the wear of the printed logo.
",
- "handle": "all-purple-everything-full-zip-hoodie",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884",
- },
- ],
- "internal": Object {
- "contentDigest": "7662506f16af28d2c6e20919de3cc7c2",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3OTYxNTk3NjI1MjA=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "26.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Sweatshirt",
- "publishedAt": "2019-03-13T23:30:56Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=",
- "tags": Array [],
- "title": "All Purple Everything Full-Zip Hoodie",
- "updatedAt": "2019-08-07T17:42:00Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0Mjc0NTA3Mzc1Mg==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTUzNDQyNA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTU5OTk2MA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY2NTQ5Ng==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY5ODI2NA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTc2MzgwMA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY0NTk4MzMyMA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY1ODUzMzQ2NA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY3MjQ1OTg2NA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY4MDU1MzU2MA==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEzMTIxMjA1Njk5NDQ=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2018-07-13T22:13:27Z",
- "description": "This 6-pack of die-cut Gatsby stickers is a great way to show off how fast your websites are. Stickers range in size from 1\\" (2.54 cm) to 3\\" (7.62 cm).",
- "descriptionHtml": "This 6-pack of die-cut Gatsby stickers is a great way to show off how fast your websites are. Stickers range in size from 1\\" (2.54 cm) to 3\\" (7.62 cm).",
- "handle": "gatsby-sticker-6-pack",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEzMTIxMjA1Njk5NDQ=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDYzMjM1Mjg1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6208.jpg?v=1555548544",
- },
- ],
- "internal": Object {
- "contentDigest": "52c444feb5908793a4a63eeca79ff8b4",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE3NTY2ODU1MDA1MDQ=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "2.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "2.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Stickers",
- "publishedAt": "2018-07-13T22:13:27Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEzMTIxMjA1Njk5NDQ=",
- "tags": Array [
- "stickers",
- ],
- "title": "Gatsby Sticker Pack",
- "updatedAt": "2019-08-09T07:52:40Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjIzOTc3Mjg3Njg4OA==",
- ],
- "vendor": "Gatsby Swag",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2019-04-05T18:30:25Z",
- "description": "A perfect triblend tank dress that is incredibly soft. Featured with our Gatsby monogram logo on the back of the dress! Care: Machine wash cold, tumble dry low.",
- "descriptionHtml": "A perfect triblend tank dress that is incredibly soft. Featured with our Gatsby monogram logo on the back of the dress!
-Care: Machine wash cold, tumble dry low.
",
- "handle": "gatsby-racerback-tank-dress",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzA1MzE2NTQ5NDM2MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dress.JPG?v=1555695165",
- },
- ],
- "internal": Object {
- "contentDigest": "352ea0e791a31273e2c181b987f5a812",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4NTY2MDg5ODkyNzI=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Tank Dress",
- "publishedAt": "2019-04-05T18:30:25Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=",
- "tags": Array [],
- "title": "Gatsby Racerback Tank Dress",
- "updatedAt": "2019-08-05T23:01:56Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjYxNTc2OA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY0ODUzNg==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY4MTMwNA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc0Njg0MA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc3OTYwOA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjgxMjM3Ng==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwNTg0MDQxNjc3Njg=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2019-04-17T22:38:22Z",
- "description": "Put your hair up in style with our satin JAMstack scrunchies! Care: Hand wash with cold water, lay flat to dry.",
- "descriptionHtml": "Put your hair up in style with our satin JAMstack scrunchies!
-Care: Hand wash with cold water, lay flat to dry.
",
- "handle": "jamstack-scrunchie",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwNTg0MDQxNjc3Njg=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0Mzc1NjcyNjM2MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6192.jpg?v=1555695177",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0Mzc1NjYyODA1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6190.jpg?v=1555695177",
- },
- ],
- "internal": Object {
- "contentDigest": "58fd339adc87b0aa51c30f9a4697dbb1",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4ODQ2NjU2NzE3Njg=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- },
- "productType": "",
- "publishedAt": "2019-04-17T22:38:22Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwNTg0MDQxNjc3Njg=",
- "tags": Array [],
- "title": "JAMstack Scrunchie",
- "updatedAt": "2019-08-05T23:17:40Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDMxNTMyMDA5MDcxMg==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2018-05-19T11:38:50Z",
- "description": "Natural process soft ink print with a black logo on a dark grey shirt. Care Instructions Machine wash cold and tumble dry only. These shirts can’t take the heat (literally)! We want to make sure you’re happy with our shirts, but they require a little TLC. Don't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!",
- "descriptionHtml": "Natural process soft ink print with a black logo on a dark grey shirt.
-Care Instructions
-Machine wash cold and tumble dry only. These shirts can’t take the heat (literally)! We want to make sure you’re happy with our shirts, but they require a little TLC.
-
-Don't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!
",
- "handle": "dark-deploy-tee",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "images": Array [
- Object {
- "altText": "Mockup of the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706",
- },
- Object {
- "altText": "Marisa wearing the Dark Deploy tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDkxNTU0NA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/marisa-dark-deploy.jpg?v=1531343717",
- },
- ],
- "internal": Object {
- "contentDigest": "8c5ecba23dee023edbe135bff4eba651",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzMwNTEwMTk5NjA1Nw==",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Shirt",
- "publishedAt": "2018-05-19T12:12:33Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==",
- "tags": Array [],
- "title": "Dark Deploy Tee",
- "updatedAt": "2019-08-08T23:41:15Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTA2ODQx",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTM5NjA5",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjM3NjMyMTUyMzgwMA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjUyMjgwODQwOA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0NTg1NzExMg==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0MjA4ODc5Mg==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MjcwNjgx",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MzAzNDQ5",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzIwODQwNzky",
- ],
- "vendor": "Gatsby Swag",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzM4MTkwNDIxNzcxMTI=": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2019-06-13T16:27:53Z",
- "description": "Subtle in the front, JAMstack party under the cap! Get your JAM on in this trucker hat.",
- "descriptionHtml": "Subtle in the front, JAMstack party under the cap! Get your JAM on in this trucker hat.",
- "handle": "jamstack-hat",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzM4MTkwNDIxNzcxMTI=",
- "images": Array [
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMTE2NzQ3MjcyMTkyODg=",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6689.jpg?v=1560443279",
- },
- Object {
- "altText": null,
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMTE2NzQ3MjcyODQ4MjQ=",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/JAmstack_hat_2.JPG?v=1560443280",
- },
- ],
- "internal": Object {
- "contentDigest": "e31bda0218e5fc546ce40a35235cef24",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzQ5OTgzMjAzMjQ2OTY=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Hat",
- "publishedAt": "2019-06-13T16:27:53Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzM4MTkwNDIxNzcxMTI=",
- "tags": Array [],
- "title": "JAMstack Hat",
- "updatedAt": "2019-08-06T15:49:35Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yOTAyOTE1MzQzOTgzMg==",
- ],
- "vendor": "Gatsby Store",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2018-06-04T00:51:11Z",
- "description": "Keep it simple with this vintage purple tee. This t-shirt is super soft and printed with a natural process that makes the print weightless. Care Instructions Machine wash cold and tumble dry only. These shirts can’t take the heat (literally)! We want to make sure you’re happy with our shirts, but they require a little TLC. Don't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!",
- "descriptionHtml": "Keep it simple with this vintage purple tee. This t-shirt is super soft and printed with a natural process that makes the print weightless.
-Care Instructions
-Machine wash cold and tumble dry only. These shirts can’t take the heat (literally)! We want to make sure you’re happy with our shirts, but they require a little TLC.
-
-Don't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!
",
- "handle": "vintage-purple-tee",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "images": Array [
- Object {
- "altText": "Mockup of the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564",
- },
- Object {
- "altText": "Action shot of Kyle wearing the Purple Logo tee.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI1MjgxMDMyOA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-kyle.jpg?v=1531344575",
- },
- ],
- "internal": Object {
- "contentDigest": "fe0346d30001661fcb81713d203ac952",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNDY4ODA2Njc3MzY=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Shirt",
- "publishedAt": "2018-06-04T00:51:11Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==",
- "tags": Array [],
- "title": "Vintage Purple Tee",
- "updatedAt": "2019-08-09T07:52:40Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjA5MTc2",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjQ0NTY4NzcwOTc4NA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjQxOTQ0",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjQ3MzAwMTA0OA==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzA3NDgw",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDMyNjUyMzk5Mg==",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzczMDE2",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzNDA1Nzg0",
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzI0OTA0MDI0",
- ],
- "vendor": "Gatsby Swag",
- },
- "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NzAyMjQ5Nzg4MA==": Object {
- "availableForSale": true,
- "children": Array [],
- "createdAt": "2018-06-12T07:00:09Z",
- "description": "Get your feet into these spaced-out black socks with a Gatsby purple border and heel. The bright pops of color keep it fun while keeping you warm. Care Instructions Keep those socks comfy on your feet and looking bright by washing them in cold water with darker colors. Tumble dry on low so they don't shrink! Don't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!",
- "descriptionHtml": "Get your feet into these spaced-out black socks with a Gatsby purple border and heel. The bright pops of color keep it fun while keeping you warm.
-Care Instructions
-Keep those socks comfy on your feet and looking bright by washing them in cold water with darker colors. Tumble dry on low so they don't shrink!
-Don't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!
",
- "handle": "space-socks",
- "id": "Shopify__Product__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NzAyMjQ5Nzg4MA==",
- "images": Array [
- Object {
- "altText": "Mockup of the Gatsby socks.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIzMjYyNTI0MA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/sock-mockup_1.jpg?v=1531343345",
- },
- Object {
- "altText": "Feet propped up on a desk wearing Gatsby socks. Photo by Jason Lengstorf",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIyOTU0NTA0OA==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/socks-on-desk_1.jpg?v=1546015563",
- },
- Object {
- "altText": "Close-up detail photo of the Gatsby socks.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIzMjY5MDc3Ng==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/sock-detail-1_1.jpg?v=1531343390",
- },
- Object {
- "altText": "Close-up detail photo of the Gatsby socks.",
- "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIzMjkyMDE1Mg==",
- "localFile___NODE": undefined,
- "originalSrc": "https://cdn.shopify.com/s/files/1/0011/5936/4633/products/sock-detail-2_1.jpg?v=1531343400",
- },
- ],
- "internal": Object {
- "contentDigest": "d38f44a19a212480a9111e291f125c4c",
- "type": "ShopifyProduct",
- },
- "metafields___NODE": Array [],
- "onlineStoreUrl": null,
- "options___NODE": Array [
- "Shopify__ProductOption__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNTAwNjU2ODQ1Njg=",
- ],
- "parent": "__SOURCE__",
- "priceRange": Object {
- "maxVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- "minVariantPrice": Object {
- "amount": "10.0",
- "currencyCode": "USD",
- },
- },
- "productType": "Socks",
- "publishedAt": "2018-06-12T07:00:09Z",
- "shopifyId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NzAyMjQ5Nzg4MA==",
- "tags": Array [],
- "title": "Space Socks",
- "updatedAt": "2019-08-09T07:32:15Z",
- "variants___NODE": Array [
- "Shopify__ProductVariant__Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjAwMjk0NzYwNTM2",
- ],
- "vendor": "Gatsby Swag",
- },
- "Shopify__Shop__undefined": Object {
- "children": Array [],
- "description": "",
- "id": "Shopify__Shop__undefined",
- "internal": Object {
- "contentDigest": "7e8bd622d5376313766d087914010149",
- "type": "ShopifyShop",
- },
- "moneyFormat": "",
- "name": "",
- "parent": "__SOURCE__",
- },
-}
-`;
diff --git a/packages/gatsby-source-shopify/src/__tests__/create-client.js b/packages/gatsby-source-shopify/src/__tests__/create-client.js
deleted file mode 100644
index 66c6ea088f8ea..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/create-client.js
+++ /dev/null
@@ -1,15 +0,0 @@
-const { createClient } = require(`../create-client`)
-
-describe(`create-client`, () => {
- it(`Allows a domain as shop name`, () => {
- expect(createClient(`my-shop.com`, `token`).url).toEqual(
- expect.not.stringContaining(`myshopify.com`)
- )
- })
-
- it(`Allows a non-domain shop name`, () => {
- expect(createClient(`my-shop`, `token`).url).toEqual(
- expect.stringContaining(`myshopify.com`)
- )
- })
-})
diff --git a/packages/gatsby-source-shopify/src/__tests__/fixtures/articles.json b/packages/gatsby-source-shopify/src/__tests__/fixtures/articles.json
deleted file mode 100644
index 86d16db125bf5..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/fixtures/articles.json
+++ /dev/null
@@ -1 +0,0 @@
-{"articles":{"pageInfo":{"hasNextPage":false},"edges":[]}}
diff --git a/packages/gatsby-source-shopify/src/__tests__/fixtures/blogs.json b/packages/gatsby-source-shopify/src/__tests__/fixtures/blogs.json
deleted file mode 100644
index 25838e5a78c31..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/fixtures/blogs.json
+++ /dev/null
@@ -1 +0,0 @@
-{"blogs":{"pageInfo":{"hasNextPage":false},"edges":[{"cursor":"eyJsYXN0X2lkIjoxMjcyMzQ4Njk3LCJsYXN0X3ZhbHVlIjoiMTI3MjM0ODY5NyJ9","node":{"id":"Z2lkOi8vc2hvcGlmeS9CbG9nLzEyNzIzNDg2OTc=","title":"News","url":"https://gatsby-swag.myshopify.com/blogs/news"}}]}}
diff --git a/packages/gatsby-source-shopify/src/__tests__/fixtures/collections.json b/packages/gatsby-source-shopify/src/__tests__/fixtures/collections.json
deleted file mode 100644
index bf3d0d1f64d7b..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/fixtures/collections.json
+++ /dev/null
@@ -1 +0,0 @@
-{"collections":{"pageInfo":{"hasNextPage":false},"edges":[{"cursor":"eyJsYXN0X2lkIjo3MDU0MDAwMTUzLCJsYXN0X3ZhbHVlIjoiNzA1NDAwMDE1MyJ9","node":{"description":"","descriptionHtml":"","handle":"frontpage","id":"Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzcwNTQwMDAxNTM=","image":null,"products":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw=="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg=="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEzMTIxMjA1Njk5NDQ="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NzAyMjQ5Nzg4MA=="}}]},"title":"Home page","updatedAt":"2019-08-09T07:35:01Z"}},{"cursor":"eyJsYXN0X2lkIjozMzc3NTc0NzE2MCwibGFzdF92YWx1ZSI6IjMzNzc1NzQ3MTYwIn0=","node":{"description":"The items in this collection are offered to contributors for free as a token of our appreciate for contributing to Gatsby.","descriptionHtml":"The items in this collection are offered to contributors for free as a token of our appreciate for contributing to Gatsby.","handle":"contributor-swag","id":"Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzMzNzc1NzQ3MTYw","image":null,"products":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw=="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg=="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NzAyMjQ5Nzg4MA=="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTU2NTI0NDA="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTUyNTkyMjQ="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzM4MTkwNDIxNzcxMTI="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwNTg0MDQxNjc3Njg="}}]},"title":"Contributor Swag","updatedAt":"2019-08-09T07:35:01Z"}},{"cursor":"eyJsYXN0X2lkIjo4ODIxNjc2NDUwNCwibGFzdF92YWx1ZSI6Ijg4MjE2NzY0NTA0In0=","node":{"description":"","descriptionHtml":"","handle":"level-2","id":"Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzg4MjE2NzY0NTA0","image":null,"products":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI="}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MjM0MTkzNDA4ODg="}}]},"title":"Level 2","updatedAt":"2019-08-08T16:05:04Z"}}]}}
diff --git a/packages/gatsby-source-shopify/src/__tests__/fixtures/pages.json b/packages/gatsby-source-shopify/src/__tests__/fixtures/pages.json
deleted file mode 100644
index 60b32e284852f..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/fixtures/pages.json
+++ /dev/null
@@ -1 +0,0 @@
-{"pages":{"pageInfo":{"hasNextPage":false},"edges":[]}}
diff --git a/packages/gatsby-source-shopify/src/__tests__/fixtures/products.json b/packages/gatsby-source-shopify/src/__tests__/fixtures/products.json
deleted file mode 100644
index 50e15ad62b899..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/fixtures/products.json
+++ /dev/null
@@ -1 +0,0 @@
-{"products":{"pageInfo":{"hasNextPage":false},"edges":[{"cursor":"eyJsYXN0X2lkIjoyMzk3Mzk5OTQxMzcsImxhc3RfdmFsdWUiOiIyMzk3Mzk5OTQxMzcifQ==","node":{"availableForSale":true,"createdAt":"2018-05-19T11:38:50Z","description":"Natural process soft ink print with a black logo on a dark grey shirt. Care Instructions Machine wash cold and tumble dry only. These shirts can’t take the heat (literally)! We want to make sure you’re happy with our shirts, but they require a little TLC. Don't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!","descriptionHtml":"Natural process soft ink print with a black logo on a dark grey shirt.
\nCare Instructions
\nMachine wash cold and tumble dry only. These shirts can’t take the heat (literally)! We want to make sure you’re happy with our shirts, but they require a little TLC.
\n\nDon't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!
","handle":"dark-deploy-tee","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIzOTczOTk5NDEzNw==","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","altText":"Mockup of the Dark Deploy tee.","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDkxNTU0NA==","altText":"Marisa wearing the Dark Deploy tee.","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/marisa-dark-deploy.jpg?v=1531343717"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzMwNTEwMTk5NjA1Nw==","name":"Size","values":["SM — Women","MD — Women","LG — Women","XL - Women","SM - Unisex","MD - Unisex","LG — Unisex","XL — Unisex","2XL — Unisex"]}],"priceRange":{"minVariantPrice":{"amount":"10.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"10.0","currencyCode":"USD"}},"productType":"Shirt","publishedAt":"2018-05-19T12:12:33Z","tags":[],"title":"Dark Deploy Tee","updatedAt":"2019-08-08T23:41:15Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTA2ODQx","image":{"altText":"Mockup of the Dark Deploy tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"},"price":"10.00","selectedOptions":[{"name":"Size","value":"SM — Women"}],"sku":"DDT-SM-W","title":"SM — Women","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MTM5NjA5","image":{"altText":"Mockup of the Dark Deploy tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"},"price":"10.00","selectedOptions":[{"name":"Size","value":"MD — Women"}],"sku":"DDT-MD-W","title":"MD — Women","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjM3NjMyMTUyMzgwMA==","image":{"altText":"Mockup of the Dark Deploy tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"},"price":"10.00","selectedOptions":[{"name":"Size","value":"LG — Women"}],"sku":"DDT-LG-W","title":"LG — Women","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjUyMjgwODQwOA==","image":{"altText":"Mockup of the Dark Deploy tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"},"price":"10.00","selectedOptions":[{"name":"Size","value":"XL - Women"}],"sku":"DDT-XL-W","title":"XL - Women","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0NTg1NzExMg==","image":{"altText":"Mockup of the Dark Deploy tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"},"price":"10.00","selectedOptions":[{"name":"Size","value":"SM - Unisex"}],"sku":"DDT-SM-M","title":"SM - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDM0MjA4ODc5Mg==","image":{"altText":"Mockup of the Dark Deploy tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"},"price":"10.00","selectedOptions":[{"name":"Size","value":"MD - Unisex"}],"sku":"DDT-MD-M","title":"MD - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MjcwNjgx","image":{"altText":"Mockup of the Dark Deploy tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"},"price":"10.00","selectedOptions":[{"name":"Size","value":"LG — Unisex"}],"sku":"DDT-LG-M","title":"LG — Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xODAwNTQ3MzAzNDQ5","image":{"altText":"Mockup of the Dark Deploy tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"},"price":"10.00","selectedOptions":[{"name":"Size","value":"XL — Unisex"}],"sku":"DDT-XL-M","title":"XL — Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzIwODQwNzky","image":{"altText":"Mockup of the Dark Deploy tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0MDM5MTI1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/dark-deploy-mockup.jpg?v=1531343706"},"price":"10.00","selectedOptions":[{"name":"Size","value":"2XL — Unisex"}],"sku":"DDT-2XL-M","title":"2XL — Unisex","weight":5,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Swag"}},{"cursor":"eyJsYXN0X2lkIjo3NjQ5MTQ2NjM1MTIsImxhc3RfdmFsdWUiOiI3NjQ5MTQ2NjM1MTIifQ==","node":{"availableForSale":true,"createdAt":"2018-06-04T00:51:11Z","description":"Keep it simple with this vintage purple tee. This t-shirt is super soft and printed with a natural process that makes the print weightless. Care Instructions Machine wash cold and tumble dry only. These shirts can’t take the heat (literally)! We want to make sure you’re happy with our shirts, but they require a little TLC. Don't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!","descriptionHtml":"Keep it simple with this vintage purple tee. This t-shirt is super soft and printed with a natural process that makes the print weightless.
\nCare Instructions
\nMachine wash cold and tumble dry only. These shirts can’t take the heat (literally)! We want to make sure you’re happy with our shirts, but they require a little TLC.
\n\nDon't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!
","handle":"vintage-purple-tee","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NDkxNDY2MzUxMg==","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","altText":"Mockup of the Purple Logo tee.","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI1MjgxMDMyOA==","altText":"Action shot of Kyle wearing the Purple Logo tee.","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-kyle.jpg?v=1531344575"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNDY4ODA2Njc3MzY=","name":"Size","values":["SM — Women","MD - Women","LG — Women","XL - Women","SM — Unisex","MD - Unisex","LG — Unisex","XL — Unisex","2XL — Unisex"]}],"priceRange":{"minVariantPrice":{"amount":"10.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"10.0","currencyCode":"USD"}},"productType":"Shirt","publishedAt":"2018-06-04T00:51:11Z","tags":[],"title":"Vintage Purple Tee","updatedAt":"2019-08-09T07:52:40Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjA5MTc2","image":{"altText":"Mockup of the Purple Logo tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"},"price":"10.00","selectedOptions":[{"name":"Size","value":"SM — Women"}],"sku":"PT-S-W","title":"SM — Women","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjQ0NTY4NzcwOTc4NA==","image":{"altText":"Mockup of the Purple Logo tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"},"price":"10.00","selectedOptions":[{"name":"Size","value":"MD - Women"}],"sku":"PT-M-W","title":"MD - Women","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMjQxOTQ0","image":{"altText":"Mockup of the Purple Logo tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"},"price":"10.00","selectedOptions":[{"name":"Size","value":"LG — Women"}],"sku":"PT-LG-W","title":"LG — Women","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUyMjQ3MzAwMTA0OA==","image":{"altText":"Mockup of the Purple Logo tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"},"price":"10.00","selectedOptions":[{"name":"Size","value":"XL - Women"}],"sku":"PT-XL-W","title":"XL - Women","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzA3NDgw","image":{"altText":"Mockup of the Purple Logo tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"},"price":"10.00","selectedOptions":[{"name":"Size","value":"SM — Unisex"}],"sku":"PT-SM-M","title":"SM — Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjcxNDMyNjUyMzk5Mg==","image":{"altText":"Mockup of the Purple Logo tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"},"price":"10.00","selectedOptions":[{"name":"Size","value":"MD - Unisex"}],"sku":"PT-MD-M","title":"MD - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzMzczMDE2","image":{"altText":"Mockup of the Purple Logo tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"},"price":"10.00","selectedOptions":[{"name":"Size","value":"LG — Unisex"}],"sku":"PT-LG-M","title":"LG — Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MTkxODkzNDA1Nzg0","image":{"altText":"Mockup of the Purple Logo tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"},"price":"10.00","selectedOptions":[{"name":"Size","value":"XL — Unisex"}],"sku":"PT-XL-M","title":"XL — Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjE0MzI0OTA0MDI0","image":{"altText":"Mockup of the Purple Logo tee.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjI0NjA2MDEyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/purple-logo-mockup.jpg?v=1531344564"},"price":"10.00","selectedOptions":[{"name":"Size","value":"2XL — Unisex"}],"sku":"PT-2XL-M","title":"2XL — Unisex","weight":5,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Swag"}},{"cursor":"eyJsYXN0X2lkIjo3NjcwMjI0OTc4ODAsImxhc3RfdmFsdWUiOiI3NjcwMjI0OTc4ODAifQ==","node":{"availableForSale":true,"createdAt":"2018-06-12T07:00:09Z","description":"Get your feet into these spaced-out black socks with a Gatsby purple border and heel. The bright pops of color keep it fun while keeping you warm. Care Instructions Keep those socks comfy on your feet and looking bright by washing them in cold water with darker colors. Tumble dry on low so they don't shrink! Don't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!","descriptionHtml":"Get your feet into these spaced-out black socks with a Gatsby purple border and heel. The bright pops of color keep it fun while keeping you warm.
\nCare Instructions
\nKeep those socks comfy on your feet and looking bright by washing them in cold water with darker colors. Tumble dry on low so they don't shrink!
\nDon't see your size? Send us an email team@gatsbyjs.com and we'll see if we can help!
","handle":"space-socks","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc2NzAyMjQ5Nzg4MA==","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIzMjYyNTI0MA==","altText":"Mockup of the Gatsby socks.","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/sock-mockup_1.jpg?v=1531343345"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIyOTU0NTA0OA==","altText":"Feet propped up on a desk wearing Gatsby socks. Photo by Jason Lengstorf","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/socks-on-desk_1.jpg?v=1546015563"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIzMjY5MDc3Ng==","altText":"Close-up detail photo of the Gatsby socks.","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/sock-detail-1_1.jpg?v=1531343390"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIzMjkyMDE1Mg==","altText":"Close-up detail photo of the Gatsby socks.","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/sock-detail-2_1.jpg?v=1531343400"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzEzNTAwNjU2ODQ1Njg=","name":"Size","values":["One size fits most"]}],"priceRange":{"minVariantPrice":{"amount":"10.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"10.0","currencyCode":"USD"}},"productType":"Socks","publishedAt":"2018-06-12T07:00:09Z","tags":[],"title":"Space Socks","updatedAt":"2019-08-09T07:32:15Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC84MjAwMjk0NzYwNTM2","image":{"altText":"Mockup of the Gatsby socks.","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzY0NjIzMjYyNTI0MA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/sock-mockup_1.jpg?v=1531343345"},"price":"10.00","selectedOptions":[{"name":"Size","value":"One size fits most"}],"sku":"S-ONE","title":"One size fits most","weight":3,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Swag"}},{"cursor":"eyJsYXN0X2lkIjoxMzEyMTIwNTY5OTQ0LCJsYXN0X3ZhbHVlIjoiMTMxMjEyMDU2OTk0NCJ9","node":{"availableForSale":true,"createdAt":"2018-07-13T22:13:27Z","description":"This 6-pack of die-cut Gatsby stickers is a great way to show off how fast your websites are. Stickers range in size from 1\" (2.54 cm) to 3\" (7.62 cm).","descriptionHtml":"This 6-pack of die-cut Gatsby stickers is a great way to show off how fast your websites are. Stickers range in size from 1\" (2.54 cm) to 3\" (7.62 cm).","handle":"gatsby-sticker-6-pack","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEzMTIxMjA1Njk5NDQ=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDYzMjM1Mjg1Ng==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6208.jpg?v=1555548544"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE3NTY2ODU1MDA1MDQ=","name":"Title","values":["Default Title"]}],"priceRange":{"minVariantPrice":{"amount":"2.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"2.0","currencyCode":"USD"}},"productType":"Stickers","publishedAt":"2018-07-13T22:13:27Z","tags":["stickers"],"title":"Gatsby Sticker Pack","updatedAt":"2019-08-09T07:52:40Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjIzOTc3Mjg3Njg4OA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDYzMjM1Mjg1Ng==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6208.jpg?v=1555548544"},"price":"2.00","selectedOptions":[{"name":"Title","value":"Default Title"}],"sku":"STICKERS-01","title":"Default Title","weight":1,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Swag"}},{"cursor":"eyJsYXN0X2lkIjoxNDAzMjU1MjU5MjI0LCJsYXN0X3ZhbHVlIjoiMTQwMzI1NTI1OTIyNCJ9","node":{"availableForSale":true,"createdAt":"2018-10-30T00:52:49Z","description":"Add more blazingly blazing speed to your wardrobe with this solid purple laundered chino twill hat. (Fine print: this is just a hat. It will not affect your speed.) Care Instructions: Spot clean with a damp cloth.","descriptionHtml":"Add more blazingly blazing speed to your wardrobe with this solid purple laundered chino twill hat. (Fine print: this is just a hat. It will not affect your speed.)
\nCare Instructions: Spot clean with a damp cloth.
","handle":"blazing-purple-hat","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTUyNTkyMjQ=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5NzEzNTQ0OA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4839_002.jpg?v=1546015349"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5NzMzMjA1Ng==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5147.jpg?v=1540860789"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5NzM5NzU5Mg==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4848.jpg?v=1540860804"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODI3MDcyODg=","name":"Title","values":["Default Title"]}],"priceRange":{"minVariantPrice":{"amount":"10.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"10.0","currencyCode":"USD"}},"productType":"Hat","publishedAt":"2018-10-30T00:52:49Z","tags":[],"title":"This Purple Hat Is Blazing Fast","updatedAt":"2019-08-07T17:39:01Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2MTYzMzExMg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5NzEzNTQ0OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4839_002.jpg?v=1546015349"},"price":"10.00","selectedOptions":[{"name":"Title","value":"Default Title"}],"sku":"1756S","title":"Default Title","weight":4,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjoxNDAzMjU1NjUyNDQwLCJsYXN0X3ZhbHVlIjoiMTQwMzI1NTY1MjQ0MCJ9","node":{"availableForSale":false,"createdAt":"2018-10-30T00:55:50Z","description":"Dark grey and black, mesh, 6-panel trucker hat with snapback closure. Made of 100% polyester linen front panels and bill. Care Instructions: Spot clean with a damp cloth.","descriptionHtml":"Dark grey and black, mesh, 6-panel trucker hat with snapback closure. Made of 100% polyester linen front panels and bill.
\nCare Instructions: Spot clean with a damp cloth.
","handle":"monogram-trucker-hat","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE0MDMyNTU2NTI0NDA=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5ODA4NTcyMA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4862.jpg?v=1540860991"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5ODE1MTI1Ng==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4855.jpg?v=1540861007"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5ODE4NDAyNA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4859_002.jpg?v=1540861027"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzE4ODQ1ODMyNjQzNDQ=","name":"Title","values":["Default Title"]}],"priceRange":{"minVariantPrice":{"amount":"10.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"10.0","currencyCode":"USD"}},"productType":"Hat","publishedAt":"2018-10-30T00:55:50Z","tags":[],"title":"Monogram Trucker Hat","updatedAt":"2019-08-05T22:55:26Z","variants":{"edges":[{"node":{"availableForSale":false,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xMjk4NzA2NTIwNDgyNA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMzkxNTc5ODA4NTcyMA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4862.jpg?v=1540860991"},"price":"10.00","selectedOptions":[{"name":"Title","value":"Default Title"}],"sku":"BA540","title":"Default Title","weight":4,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjoxNTY2MjY5OTk3MTQ0LCJsYXN0X3ZhbHVlIjoiMTU2NjI2OTk5NzE0NCJ9","node":{"availableForSale":true,"createdAt":"2018-11-16T19:36:04Z","description":"Show us how you rock your custom Gatsby lapel pin! Silver plating, clois-tech lapel pin with a clutch attachment. Size – 0.835” Care Instructions: Please do not machine wash.","descriptionHtml":"Show us how you rock your custom Gatsby lapel pin! Silver plating, clois-tech lapel pin with a clutch attachment. Size – 0.835”
\nCare Instructions: Please do not machine wash.
","handle":"gatsby-lapel-pin","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE1NjYyNjk5OTcxNDQ=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNDQ5ODk5NTI4MjAwOA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4947.png?v=1542402545"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNDQ5ODYzNTQ1NjYwMA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4972.jpg?v=1542402545"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNDQ5ODYzNjgwMDA4OA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4956.jpg?v=1542402545"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzIxMzUwODQ1NjQ1Njg=","name":"Title","values":["Default Title"]}],"priceRange":{"minVariantPrice":{"amount":"2.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"2.0","currencyCode":"USD"}},"productType":"Pin","publishedAt":"2018-11-16T19:36:04Z","tags":[],"title":"Gatsby Lapel Pin","updatedAt":"2019-08-07T13:10:10Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xNTM5ODYzOTc5NjMxMg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNDQ5ODk5NTI4MjAwOA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_4947.png?v=1542402545"},"price":"2.00","selectedOptions":[{"name":"Title","value":"Default Title"}],"sku":"lapelpin","title":"Default Title","weight":0.5,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjoxOTIzNDE5MzQwODg4LCJsYXN0X3ZhbHVlIjoiMTkyMzQxOTM0MDg4OCJ9","node":{"availableForSale":true,"createdAt":"2018-12-05T01:12:21Z","description":"Keep your hot beverages BLAZING hot for hours with this stainless vacuum-insulated Fifty/Fifty 12 oz/345mL bottle. Also works to keep cold beverages, er, blazing cold? Care Instructions: Do not put in microwave, freezer, or dishwasher. Hand wash with hot soapy water. Leave cap off and allow to air dry. Do not use cleaners containing bleach or chlorine.","descriptionHtml":"Keep your hot beverages BLAZING hot for hours with this stainless vacuum-insulated Fifty/Fifty 12 oz/345mL bottle. Also works to keep cold beverages, er, blazing cold?
\nCare Instructions: Do not put in microwave, freezer, or dishwasher. Hand wash with hot soapy water. Leave cap off and allow to air dry. Do not use cleaners containing bleach or chlorine.
","handle":"12oz-travel-mug","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MjM0MTkzNDA4ODg=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjYwMTc4ODM5MTUxMg==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG-5659.png?v=1543972353"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjYwMTc4ODQ1NzA0OA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG-5667.JPG?v=1543972362"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjYwMTc4ODU1NTM1Mg==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG-5664.JPG?v=1543972368"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI2OTk3ODgzODYzOTI=","name":"Title","values":["Default Title"]}],"priceRange":{"minVariantPrice":{"amount":"26.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"26.0","currencyCode":"USD"}},"productType":"Water Bottle","publishedAt":"2018-12-05T01:12:21Z","tags":[],"title":"Mug for (Blazing) Hot Beverages","updatedAt":"2019-08-06T11:22:51Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTQ1MjAwMDczMTIyNA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjYwMTc4ODM5MTUxMg==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG-5659.png?v=1543972353"},"price":"26.00","selectedOptions":[{"name":"Title","value":"Default Title"}],"sku":"V-12-00002","title":"Default Title","weight":6,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjoxOTMyNjk0NTg1NDMyLCJsYXN0X3ZhbHVlIjoiMTkzMjY5NDU4NTQzMiJ9","node":{"availableForSale":true,"createdAt":"2018-12-28T22:36:24Z","description":"For the work-from-home professional, Gatsby's Freelance Pants take “business casual” to a whole new level. These slick black pants will help you stay calm and reach #maximumcomf even when you're under pressure.","descriptionHtml":"For the work-from-home professional, Gatsby's Freelance Pants take “business casual” to a whole new level. These slick black pants will help you stay calm and reach #maximumcomf even when you're under pressure.","handle":"freelance-pants","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTQ1ODU0MzI=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjg2ODMwOTY=","name":"Size","values":["XS - Unisex","SM - Unisex","MD - Unisex","L - Unisex","XL - Unisex","2XL - Unisex"]}],"priceRange":{"minVariantPrice":{"amount":"26.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"26.0","currencyCode":"USD"}},"productType":"Pajama Pants","publishedAt":"2018-12-28T22:36:24Z","tags":[],"title":"Freelance Pants","updatedAt":"2019-08-07T07:53:05Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTg5Mjk1Mg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613"},"price":"26.00","selectedOptions":[{"name":"Size","value":"XS - Unisex"}],"sku":"FLP-1","title":"XS - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTkyNTcyMA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613"},"price":"26.00","selectedOptions":[{"name":"Size","value":"SM - Unisex"}],"sku":"FLP-2","title":"SM - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk1ODQ4OA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613"},"price":"26.00","selectedOptions":[{"name":"Size","value":"MD - Unisex"}],"sku":"FLP-3","title":"MD - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NTk5MTI1Ng==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613"},"price":"26.00","selectedOptions":[{"name":"Size","value":"L - Unisex"}],"sku":"FLP-4","title":"L - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjAyNDAyNA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613"},"price":"26.00","selectedOptions":[{"name":"Size","value":"XL - Unisex"}],"sku":"FLP-5","title":"XL - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":false,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NjA1Njc5Mg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTA4NDUwNA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5137.png?v=1546036613"},"price":"26.00","selectedOptions":[{"name":"Size","value":"2XL - Unisex"}],"sku":"FLP-6","title":"2XL - Unisex","weight":5,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjoxOTMyNjk1MDc2OTUyLCJsYXN0X3ZhbHVlIjoiMTkzMjY5NTA3Njk1MiJ9","node":{"availableForSale":true,"createdAt":"2018-12-28T22:43:00Z","description":"Be the life of your own comfy party with these purple and playful JAMstack Jammies. Did we mention they have pockets?","descriptionHtml":"Be the life of your own comfy party with these purple and playful JAMstack Jammies. Did we mention they have pockets?","handle":"jamstack-jammies","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5MzI2OTUwNzY5NTI=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3MTI1Mjk2MzMzNjg=","name":"Size","values":["XS - Unisex","SM - Unisex","MD - Unisex","L - Unisex","XL - Unisex","2XL - Unisex"]}],"priceRange":{"minVariantPrice":{"amount":"26.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"26.0","currencyCode":"USD"}},"productType":"Pajama Pants","publishedAt":"2018-12-28T22:43:00Z","tags":[],"title":"JAMstack Jammies","updatedAt":"2019-08-08T16:03:30Z","variants":{"edges":[{"node":{"availableForSale":false,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzA3MjYwMA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022"},"price":"26.00","selectedOptions":[{"name":"Size","value":"XS - Unisex"}],"sku":"JSJP-1","title":"XS - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEwNTM2OA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022"},"price":"26.00","selectedOptions":[{"name":"Size","value":"SM - Unisex"}],"sku":"JSJP-2","title":"SM - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":false,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzEzODEzNg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022"},"price":"26.00","selectedOptions":[{"name":"Size","value":"MD - Unisex"}],"sku":"JSJP-3","title":"MD - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzE3MDkwNA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022"},"price":"26.00","selectedOptions":[{"name":"Size","value":"L - Unisex"}],"sku":"JSJP-4","title":"L - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIwMzY3Mg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022"},"price":"26.00","selectedOptions":[{"name":"Size","value":"XL - Unisex"}],"sku":"JSJP-5","title":"XL - Unisex","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":false,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTUwOTk0NzIzNjQ0MA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjY0NjYwOTkzNjQ3Mg==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_5136.png?v=1546037022"},"price":"26.00","selectedOptions":[{"name":"Size","value":"2XL - Unisex"}],"sku":"JSJP-6","title":"2XL - Unisex","weight":5,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjoxOTc0NDMyMjAyODQwLCJsYXN0X3ZhbHVlIjoiMTk3NDQzMjIwMjg0MCJ9","node":{"availableForSale":true,"createdAt":"2019-03-01T16:53:32Z","description":"This fleece zip hoodie is perfect for keeping cozy! Featured with drawcords and split kangaroo pockets. Material: 52/48 Airlume combed and ringspun cotton/poly fleece. Care Instructions: Machine wash cold, tumble dry low.","descriptionHtml":"This fleece zip hoodie is perfect for keeping cozy! Featured with drawcords and split kangaroo pockets. Material: 52/48 Airlume combed and ringspun cotton/poly fleece.
\nCare Instructions: Machine wash cold, tumble dry low.
","handle":"dark-monogram-hoodie","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5NzQ0MzIyMDI4NDA=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3NzExMDcwODY0MjQ=","name":"Size","values":["XS - Unisex","S - Unisex","M - Unisex","L - Unisex","XL - Unisex","2XL - Unisex"]}],"priceRange":{"minVariantPrice":{"amount":"26.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"26.0","currencyCode":"USD"}},"productType":"Sweatshirt","publishedAt":"2019-03-01T16:53:32Z","tags":[],"title":"Dark Monogram Hoodie","updatedAt":"2019-08-07T18:18:24Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MTc0MzcwNA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115"},"price":"26.00","selectedOptions":[{"name":"Size","value":"XS - Unisex"}],"sku":"AA9590XS","title":"XS - Unisex","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0MjU5NTY3Mg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115"},"price":"26.00","selectedOptions":[{"name":"Size","value":"S - Unisex"}],"sku":"AA9590S","title":"S - Unisex","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":false,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NjM5Njc2MA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115"},"price":"26.00","selectedOptions":[{"name":"Size","value":"M - Unisex"}],"sku":"AA9590M","title":"M - Unisex","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":false,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0Njc4OTk3Ng==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115"},"price":"26.00","selectedOptions":[{"name":"Size","value":"L - Unisex"}],"sku":"AA9590L","title":"L - Unisex","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0NzI4MTQ5Ng==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115"},"price":"26.00","selectedOptions":[{"name":"Size","value":"XL - Unisex"}],"sku":"AA9590XL","title":"XL - Unisex","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTcwOTk0ODEzMzQ2NA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE5NTYzODA3MTM4NA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dark_Hoodie.jpg?v=1556318115"},"price":"26.00","selectedOptions":[{"name":"Size","value":"2XL - Unisex"}],"sku":"AA95902XL","title":"2XL - Unisex","weight":7,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjoxOTkyNjMyMDQxNTYwLCJsYXN0X3ZhbHVlIjoiMTk5MjYzMjA0MTU2MCJ9","node":{"availableForSale":true,"createdAt":"2019-03-13T23:30:56Z","description":"Say “no” to boring colors and go all purple everything with this comfy cotton-poly blend hoodie. Rib knit cuff with thumb exits, a rib knit hem, an interior phone pocket, and headphone cord port. Care Instructions: Super hot water and high dryer heat may shrink the sweatshirt due to the cotton content. Suggested to turn inside out while washing to protect the wear of the printed logo.","descriptionHtml":"Say “no” to boring colors and go all purple everything with this comfy cotton-poly blend hoodie. Rib knit cuff with thumb exits, a rib knit hem, an interior phone pocket, and headphone cord port.
\nCare Instructions: Super hot water and high dryer heat may shrink the sweatshirt due to the cotton content. Suggested to turn inside out while washing to protect the wear of the printed logo.
","handle":"all-purple-everything-full-zip-hoodie","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzE5OTI2MzIwNDE1NjA=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI3OTYxNTk3NjI1MjA=","name":"Size","values":["S - Men","MD - Men","LG - Men","XL - Men","2XL - Men","3XL - Men","S - Womens","MD - Womens","LG - Womens","XL - Womens"]}],"priceRange":{"minVariantPrice":{"amount":"26.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"26.0","currencyCode":"USD"}},"productType":"Sweatshirt","publishedAt":"2019-03-13T23:30:56Z","tags":[],"title":"All Purple Everything Full-Zip Hoodie","updatedAt":"2019-08-07T17:42:00Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0Mjc0NTA3Mzc1Mg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"S - Men"}],"sku":"TM18135S","title":"S - Men","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTUzNDQyNA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"MD - Men"}],"sku":"TM98139","title":"MD - Men","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTU5OTk2MA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"LG - Men"}],"sku":"TM98140","title":"LG - Men","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY2NTQ5Ng==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"XL - Men"}],"sku":"TM98141","title":"XL - Men","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTY5ODI2NA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"2XL - Men"}],"sku":"TM98142","title":"2XL - Men","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjU3MTc2MzgwMA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"3XL - Men"}],"sku":"TM98143","title":"3XL - Men","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY0NTk4MzMyMA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"S - Womens"}],"sku":"TM18135S","title":"S - Womens","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY1ODUzMzQ2NA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"MD - Womens"}],"sku":"TM18135M","title":"MD - Womens","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY3MjQ1OTg2NA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"LG - Womens"}],"sku":"TM18135","title":"LG - Womens","weight":7,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8xOTg0MjY4MDU1MzU2MA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNjg5NzUxMTk1NjU2OA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6058.jpg?v=1552519884"},"price":"26.00","selectedOptions":[{"name":"Size","value":"XL - Womens"}],"sku":"TM18135","title":"XL - Womens","weight":7,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjoyMDM2ODIxMjI5NjU2LCJsYXN0X3ZhbHVlIjoiMjAzNjgyMTIyOTY1NiJ9","node":{"availableForSale":true,"createdAt":"2019-04-05T18:30:25Z","description":"A perfect triblend tank dress that is incredibly soft. Featured with our Gatsby monogram logo on the back of the dress! Care: Machine wash cold, tumble dry low.","descriptionHtml":"A perfect triblend tank dress that is incredibly soft. Featured with our Gatsby monogram logo on the back of the dress!
\nCare: Machine wash cold, tumble dry low.
","handle":"gatsby-racerback-tank-dress","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwMzY4MjEyMjk2NTY=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzA1MzE2NTQ5NDM2MA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/Gatsby_Dress.JPG?v=1555695165"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4NTY2MDg5ODkyNzI=","name":"Size","values":["X-Small","Small","Medium","Large","X-Large","XX-Large"]}],"priceRange":{"minVariantPrice":{"amount":"10.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"10.0","currencyCode":"USD"}},"productType":"Tank Dress","publishedAt":"2019-04-05T18:30:25Z","tags":[],"title":"Gatsby Racerback Tank Dress","updatedAt":"2019-08-05T23:01:56Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjYxNTc2OA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165"},"price":"10.00","selectedOptions":[{"name":"Size","value":"X-Small"}],"sku":"6734","title":"X-Small","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY0ODUzNg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165"},"price":"10.00","selectedOptions":[{"name":"Size","value":"Small"}],"sku":"6735","title":"Small","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjY4MTMwNA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165"},"price":"10.00","selectedOptions":[{"name":"Size","value":"Medium"}],"sku":"6736","title":"Medium","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc0Njg0MA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165"},"price":"10.00","selectedOptions":[{"name":"Size","value":"Large"}],"sku":"6737","title":"Large","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5Mjc3OTYwOA==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165"},"price":"10.00","selectedOptions":[{"name":"Size","value":"X-Large"}],"sku":"6738","title":"X-Large","weight":5,"weightUnit":"OUNCES"}},{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDIwNjA5MjgxMjM3Ng==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0NDM0MDMyNDQ0MA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6198.jpg?v=1555695165"},"price":"10.00","selectedOptions":[{"name":"Size","value":"XX-Large"}],"sku":"6739","title":"XX-Large","weight":5,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjoyMDU4NDA0MTY3NzY4LCJsYXN0X3ZhbHVlIjoiMjA1ODQwNDE2Nzc2OCJ9","node":{"availableForSale":true,"createdAt":"2019-04-17T22:38:22Z","description":"Put your hair up in style with our satin JAMstack scrunchies! Care: Hand wash with cold water, lay flat to dry.","descriptionHtml":"Put your hair up in style with our satin JAMstack scrunchies!
\nCare: Hand wash with cold water, lay flat to dry.
","handle":"jamstack-scrunchie","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzIwNTg0MDQxNjc3Njg=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0Mzc1NjcyNjM2MA==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6192.jpg?v=1555695177"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0Mzc1NjYyODA1Ng==","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6190.jpg?v=1555695177"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzI4ODQ2NjU2NzE3Njg=","name":"Title","values":["Default Title"]}],"priceRange":{"minVariantPrice":{"amount":"10.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"10.0","currencyCode":"USD"}},"productType":"","publishedAt":"2019-04-17T22:38:22Z","tags":[],"title":"JAMstack Scrunchie","updatedAt":"2019-08-05T23:17:40Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yMDMxNTMyMDA5MDcxMg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvNzE0Mzc1NjcyNjM2MA==","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6192.jpg?v=1555695177"},"price":"10.00","selectedOptions":[{"name":"Title","value":"Default Title"}],"sku":"SCNCH","title":"Default Title","weight":0.5,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}},{"cursor":"eyJsYXN0X2lkIjozODE5MDQyMTc3MTEyLCJsYXN0X3ZhbHVlIjoiMzgxOTA0MjE3NzExMiJ9","node":{"availableForSale":true,"createdAt":"2019-06-13T16:27:53Z","description":"Subtle in the front, JAMstack party under the cap! Get your JAM on in this trucker hat.","descriptionHtml":"Subtle in the front, JAMstack party under the cap! Get your JAM on in this trucker hat.","handle":"jamstack-hat","id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzM4MTkwNDIxNzcxMTI=","images":{"edges":[{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMTE2NzQ3MjcyMTkyODg=","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6689.jpg?v=1560443279"}},{"node":{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMTE2NzQ3MjcyODQ4MjQ=","altText":null,"originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/JAmstack_hat_2.JPG?v=1560443280"}}]},"metafields":{"edges":[]},"onlineStoreUrl":null,"options":[{"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0T3B0aW9uLzQ5OTgzMjAzMjQ2OTY=","name":"Title","values":["Default Title"]}],"priceRange":{"minVariantPrice":{"amount":"10.0","currencyCode":"USD"},"maxVariantPrice":{"amount":"10.0","currencyCode":"USD"}},"productType":"Hat","publishedAt":"2019-06-13T16:27:53Z","tags":[],"title":"JAMstack Hat","updatedAt":"2019-08-06T15:49:35Z","variants":{"edges":[{"node":{"availableForSale":true,"compareAtPrice":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8yOTAyOTE1MzQzOTgzMg==","image":{"altText":null,"id":"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0SW1hZ2UvMTE2NzQ3MjcyMTkyODg=","originalSrc":"https://cdn.shopify.com/s/files/1/0011/5936/4633/products/IMG_6689.jpg?v=1560443279"},"price":"10.00","selectedOptions":[{"name":"Title","value":"Default Title"}],"sku":"jamstackhat","title":"Default Title","weight":4,"weightUnit":"OUNCES"}}]},"vendor":"Gatsby Store"}}]}}
diff --git a/packages/gatsby-source-shopify/src/__tests__/fixtures/shop-details.json b/packages/gatsby-source-shopify/src/__tests__/fixtures/shop-details.json
deleted file mode 100644
index 089bc8406334d..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/fixtures/shop-details.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "shop": { "description": "", "name": "", "moneyFormat": "" } }
\ No newline at end of file
diff --git a/packages/gatsby-source-shopify/src/__tests__/fixtures/shop-policies.json b/packages/gatsby-source-shopify/src/__tests__/fixtures/shop-policies.json
deleted file mode 100644
index 01df293312e37..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/fixtures/shop-policies.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "shop": { "privacyPolicy": null, "refundPolicy": null, "termsOfService": null } }
diff --git a/packages/gatsby-source-shopify/src/__tests__/index.js b/packages/gatsby-source-shopify/src/__tests__/index.js
deleted file mode 100644
index 9b9bbe73a6db1..0000000000000
--- a/packages/gatsby-source-shopify/src/__tests__/index.js
+++ /dev/null
@@ -1,84 +0,0 @@
-jest.mock(`gatsby-source-filesystem`, () => {
- return {
- createRemoteFileNode: jest.fn(),
- }
-})
-
-import * as mockQueries from "../queries"
-jest.mock(`../create-client`, () => {
- return {
- createClient: jest.fn(() => {
- return {
- request: async query => {
- // Hack alert. match query text, from that get query name (like SHOP_POLICIES_QUERY) and convert to filename like policies.json
- const fixturePathFromQuery = (query, mockQueries) => {
- const [queryName] = Object.entries(mockQueries).find(
- ([queryName, queryString]) => queryString === query
- )
- const jsonFile = queryName
- .split(`_`)
- .map(el => el.toLowerCase())
- .filter(el => el !== `query`)
- .join(`-`)
- return jsonFile + `.json`
- }
- const jsonFile = fixturePathFromQuery(query, mockQueries)
- return require(`./fixtures/${jsonFile}`)
- },
- }
- }),
- }
-})
-
-const { sourceNodes } = require(`../gatsby-node`)
-
-describe(`gatsby-source-shopify`, () => {
- /**
- * This test is pretty bare-bones. Among other things:
- *
- * - Some of the fixtures are empty responses
- * - There's no pagination testing
- * - There's no validation that nodes are created correctly, other than a snapshot
- * - There's no way to test different responses for the same query
- *
- * TODO: more and better tests
- *
- * Mocking setup is borrowed from gatsby-source-drupal
- */
- const nodes = {}
- const actions = {
- createNode: jest.fn(node => (nodes[node.id] = node)),
- }
-
- const activity = {
- start: jest.fn(),
- end: jest.fn(),
- }
-
- const reporter = {
- info: jest.fn(),
- activityTimer: jest.fn(() => activity),
- log: jest.fn(),
- }
-
- const cache = {
- get: jest.fn(),
- }
-
- const args = {
- actions,
- reporter,
- cache,
- // getNode: id => nodes[id],
- }
-
- beforeAll(async () => {
- await sourceNodes(args, { shopName: `test-shop` })
- })
-
- it(`Generates nodes`, () => {
- expect(Object.keys(nodes).length).not.toEqual(0)
- // better than no tests? ¯\_(ツ)_/¯
- expect(nodes).toMatchSnapshot()
- })
-})
diff --git a/packages/gatsby-source-shopify/src/client.ts b/packages/gatsby-source-shopify/src/client.ts
new file mode 100644
index 0000000000000..00b4007554113
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/client.ts
@@ -0,0 +1,48 @@
+import fetch from "node-fetch"
+import { HttpError } from "./errors"
+
+const adminUrl = (options: ShopifyPluginOptions): string =>
+ `https://@${options.storeUrl}/admin/api/2021-01/graphql.json`
+
+const MAX_BACKOFF_MILLISECONDS = 60000
+
+interface IGraphQLClient {
+ request: (query: string, variables?: Record) => Promise
+}
+
+export function createClient(options: ShopifyPluginOptions): IGraphQLClient {
+ const url = adminUrl(options)
+
+ async function graphqlFetch(
+ query: string,
+ variables?: Record,
+ retries = 0
+ ): Promise {
+ const response = await fetch(url, {
+ method: `POST`,
+ headers: {
+ "Content-Type": `application/json`,
+ "X-Shopify-Access-Token": options.password,
+ },
+ body: JSON.stringify({
+ query,
+ variables,
+ }),
+ })
+
+ if (!response.ok) {
+ const waitTime = 2 ** (retries + 1) + 500
+ if (response.status >= 500 && waitTime < MAX_BACKOFF_MILLISECONDS) {
+ await new Promise(resolve => setTimeout(resolve, waitTime))
+ return graphqlFetch(query, variables, retries + 1)
+ }
+
+ throw new HttpError(response)
+ }
+
+ const json = await response.json()
+ return json.data as T
+ }
+
+ return { request: graphqlFetch }
+}
diff --git a/packages/gatsby-source-shopify/src/constants.js b/packages/gatsby-source-shopify/src/constants.js
deleted file mode 100644
index 7265e6a20b62a..0000000000000
--- a/packages/gatsby-source-shopify/src/constants.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// Node prefix
-export const TYPE_PREFIX = `Shopify`
-
-// Node types
-export const ARTICLE = `Article`
-export const BLOG = `Blog`
-export const COLLECTION = `Collection`
-export const COMMENT = `Comment`
-export const PRODUCT = `Product`
-export const PRODUCT_OPTION = `ProductOption`
-export const PRODUCT_VARIANT = `ProductVariant`
-export const PRODUCT_METAFIELD = `ProductMetafield`
-export const PRODUCT_VARIANT_METAFIELD = `ProductVariantMetafield`
-export const SHOP_POLICY = `ShopPolicy`
-export const SHOP_DETAILS = `Shop`
-export const PAGE = `Page`
-export const SHOP = `shop`
-export const CONTENT = `content`
-
-export const NODE_TO_ENDPOINT_MAPPING = {
- [ARTICLE]: `articles`,
- [BLOG]: `blogs`,
- [COLLECTION]: `collections`,
- [PRODUCT]: `products`,
- [PAGE]: `pages`,
-}
diff --git a/packages/gatsby-source-shopify/src/create-client.js b/packages/gatsby-source-shopify/src/create-client.js
deleted file mode 100644
index aa9a7c0e61bec..0000000000000
--- a/packages/gatsby-source-shopify/src/create-client.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { GraphQLClient } from "graphql-request"
-/**
- * Create a Shopify Storefront GraphQL client for the provided name and token.
- */
-export const createClient = (shopName, accessToken, apiVersion) => {
- let url
- if (shopName.includes(`.`)) {
- url = `https://${shopName}/api/${apiVersion}/graphql.json`
- } else {
- url = `https://${shopName}.myshopify.com/api/${apiVersion}/graphql.json`
- }
- return new GraphQLClient(url, {
- headers: {
- "X-Shopify-Storefront-Access-Token": accessToken,
- },
- })
-}
diff --git a/packages/gatsby-source-shopify/src/create-schema-customization.ts b/packages/gatsby-source-shopify/src/create-schema-customization.ts
new file mode 100644
index 0000000000000..e430549fa26d9
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/create-schema-customization.ts
@@ -0,0 +1,295 @@
+import {
+ CreateSchemaCustomizationArgs,
+ NodePluginSchema,
+ GatsbyGraphQLObjectType,
+} from "../../gatsby"
+
+function addFields(
+ def: GatsbyGraphQLObjectType,
+ fields: GatsbyGraphQLObjectType["config"]["fields"]
+): void {
+ def.config.fields = {
+ ...(def.config.fields || {}),
+ ...fields,
+ }
+}
+
+function defineImageNode(
+ name: string,
+ schema: NodePluginSchema,
+ pluginOptions: ShopifyPluginOptions,
+ fields: GatsbyGraphQLObjectType["config"]["fields"] = {}
+): GatsbyGraphQLObjectType {
+ const imageDef = schema.buildObjectType({
+ name,
+ })
+
+ if (pluginOptions.downloadImages) {
+ imageDef.config.fields = {
+ localFile: {
+ type: `File`,
+ extensions: {
+ link: {},
+ },
+ },
+ }
+ }
+
+ addFields(imageDef, {
+ ...fields,
+ altText: `String`,
+ height: `Int`,
+ id: `String`,
+ originalSrc: `String!`,
+ transformedSrc: `String!`,
+ width: `Int`,
+ })
+
+ return imageDef
+}
+
+export function createSchemaCustomization(
+ { actions, schema }: CreateSchemaCustomizationArgs,
+ pluginOptions: ShopifyPluginOptions
+): void {
+ const includeCollections = pluginOptions.shopifyConnections?.includes(
+ `collections`
+ )
+
+ const includeOrders = pluginOptions.shopifyConnections?.includes(`orders`)
+
+ const name = (name: string): string =>
+ `${pluginOptions.typePrefix || ``}${name}`
+
+ const sharedMetafieldFields: GatsbyGraphQLObjectType["config"]["fields"] = {
+ createdAt: `Date!`,
+ description: `String`,
+ id: `ID!`,
+ key: `String!`,
+ namespace: `String!`,
+ ownerType: `String!`,
+ updatedAt: `Date!`,
+ value: `String!`,
+ valueType: `String!`,
+ }
+
+ const metafieldInterface = schema.buildInterfaceType({
+ name: name(`ShopifyMetafieldInterface`),
+ fields: sharedMetafieldFields,
+ // @ts-ignore TODO: Once Gatsby Core updates its graphql-compose package to a version that has the right
+ // types to support interfaces implementing other interfaces, remove the ts-ignore
+ interfaces: [`Node`],
+ })
+
+ const metafieldOwnerTypes = [`Product`, `ProductVariant`]
+ if (includeCollections) {
+ metafieldOwnerTypes.push(`Collection`)
+ }
+
+ const metafieldTypes = metafieldOwnerTypes.map((ownerType: string) => {
+ const parentKey = ownerType.charAt(0).toLowerCase() + ownerType.slice(1)
+ return schema.buildObjectType({
+ name: name(`Shopify${ownerType}Metafield`),
+ fields: {
+ ...sharedMetafieldFields,
+ [parentKey]: {
+ type: name(`Shopify${ownerType}`),
+ extensions: {
+ link: {
+ from: `${parentKey}Id`,
+ by: `id`,
+ },
+ },
+ },
+ },
+ interfaces: [`Node`, name(`ShopifyMetafieldInterface`)],
+ })
+ })
+
+ const productDef = schema.buildObjectType({
+ name: name(`ShopifyProduct`),
+ fields: {
+ variants: {
+ type: `[${name(`ShopifyProductVariant`)}]`,
+ extensions: {
+ link: {
+ from: `id`,
+ by: `productId`,
+ },
+ },
+ },
+ metafields: {
+ type: `[${name(`ShopifyProductMetafield`)}]`,
+ extensions: {
+ link: {
+ from: `id`,
+ by: `productId`,
+ },
+ },
+ },
+ images: {
+ type: `[${name(`ShopifyProductImage`)}]`,
+ extensions: {
+ link: {
+ from: `id`,
+ by: `productId`,
+ },
+ },
+ },
+ },
+ interfaces: [`Node`],
+ })
+
+ const productImageDef = defineImageNode(
+ name(`ShopifyProductImage`),
+ schema,
+ pluginOptions,
+ {
+ product: {
+ type: name(`ShopifyProduct!`),
+ extensions: {
+ link: {
+ from: `productId`,
+ by: `id`,
+ },
+ },
+ },
+ }
+ )
+
+ productImageDef.config.interfaces = [`Node`]
+
+ if (includeCollections) {
+ addFields(productDef, {
+ collections: {
+ type: `[${name(`ShopifyCollection`)}]`,
+ extensions: {
+ link: {
+ from: `id`,
+ by: `productIds`,
+ },
+ },
+ },
+ })
+ }
+
+ const typeDefs = [
+ productDef,
+ productImageDef,
+ schema.buildObjectType({
+ name: name(`ShopifyProductVariant`),
+ fields: {
+ product: {
+ type: name(`ShopifyProduct!`),
+ extensions: {
+ link: {
+ from: `productId`,
+ by: `id`,
+ },
+ },
+ },
+ metafields: {
+ type: `[${name(`ShopifyProductVariantMetafield`)}]`,
+ extensions: {
+ link: {
+ from: `id`,
+ by: `productVariantId`,
+ },
+ },
+ },
+ },
+ interfaces: [`Node`],
+ }),
+ metafieldInterface,
+ ...metafieldTypes,
+ ]
+
+ if (includeCollections) {
+ typeDefs.push(
+ schema.buildObjectType({
+ name: name(`ShopifyCollection`),
+ fields: {
+ products: {
+ type: `[${name(`ShopifyProduct`)}]`,
+ extensions: {
+ link: {
+ from: `productIds`,
+ by: `id`,
+ },
+ },
+ },
+ metafields: {
+ type: `[${name(`ShopifyCollectionMetafield`)}]`,
+ extensions: {
+ link: {
+ from: `id`,
+ by: `collectionId`,
+ },
+ },
+ },
+ },
+ interfaces: [`Node`],
+ })
+ )
+ }
+
+ if (includeOrders) {
+ typeDefs.push(
+ schema.buildObjectType({
+ name: name(`ShopifyOrder`),
+ fields: {
+ lineItems: {
+ type: `[${name(`ShopifyLineItem`)}]`,
+ extensions: {
+ link: {
+ from: `id`,
+ by: `orderId`,
+ },
+ },
+ },
+ },
+ interfaces: [`Node`],
+ }),
+ schema.buildObjectType({
+ name: name(`ShopifyLineItem`),
+ fields: {
+ product: {
+ type: name(`ShopifyProduct`),
+ extensions: {
+ link: {
+ from: `productId`,
+ by: `id`,
+ },
+ },
+ },
+ order: {
+ type: name(`ShopifyOrder!`),
+ extensions: {
+ link: {
+ from: `orderId`,
+ by: `id`,
+ },
+ },
+ },
+ },
+ interfaces: [`Node`],
+ })
+ )
+ }
+
+ typeDefs.push(
+ ...[
+ `ShopifyProductFeaturedImage`,
+ `ShopifyProductFeaturedMediaPreviewImage`,
+ `ShopifyProductVariantImage`,
+ ].map(typeName => defineImageNode(name(typeName), schema, pluginOptions))
+ )
+
+ if (includeCollections) {
+ typeDefs.push(
+ defineImageNode(name(`ShopifyCollectionImage`), schema, pluginOptions)
+ )
+ }
+
+ actions.createTypes(typeDefs)
+}
diff --git a/packages/gatsby-source-shopify/src/errors.ts b/packages/gatsby-source-shopify/src/errors.ts
new file mode 100644
index 0000000000000..585f87684cbb5
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/errors.ts
@@ -0,0 +1,34 @@
+import { Response } from "node-fetch"
+
+export const pluginErrorCodes = {
+ bulkOperationFailed: `111000`,
+ unknownSourcingFailure: `111001`,
+ unknownApiError: `111002`,
+
+ apiConflict: `111003`,
+}
+
+export class OperationError extends Error {
+ public node: BulkOperationNode
+
+ constructor(node: BulkOperationNode) {
+ const { errorCode, id } = node
+ super(`Operation ${id} failed with ${errorCode}`)
+
+ this.node = node
+
+ Error.captureStackTrace(this, OperationError)
+ }
+}
+
+export class HttpError extends Error {
+ public response: Response
+
+ constructor(response: Response) {
+ super(response.statusText)
+
+ this.response = response
+
+ Error.captureStackTrace(this, HttpError)
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/events.ts b/packages/gatsby-source-shopify/src/events.ts
new file mode 100644
index 0000000000000..4e86e8392e244
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/events.ts
@@ -0,0 +1,58 @@
+import { makeShopifyFetch } from "./rest"
+
+interface IEvent {
+ subject_id: number
+ subject_type: string
+}
+
+export function eventsApi(
+ options: ShopifyPluginOptions
+): {
+ fetchDestroyEventsSince: (date: Date) => Promise>
+} {
+ const shopifyFetch = makeShopifyFetch(options)
+
+ return {
+ async fetchDestroyEventsSince(date): Promise> {
+ let resp = await shopifyFetch(
+ `/events.json?limit=250&verb=destroy&created_at_min=${date.toISOString()}`
+ )
+
+ const { events } = await resp.json()
+
+ let gatherPaginatedEvents = true
+
+ while (gatherPaginatedEvents) {
+ const paginationInfo = resp.headers.get(`link`)
+ if (!paginationInfo) {
+ gatherPaginatedEvents = false
+ break
+ }
+
+ const pageLinks: Array<{
+ url: string
+ rel: string
+ }> = paginationInfo.split(`,`).map((pageData: string) => {
+ const [, url, rel] = pageData.match(/<(.*)>; rel="(.*)"/) || []
+ return {
+ url,
+ rel,
+ }
+ })
+
+ const nextPage = pageLinks.find(l => l.rel === `next`)
+
+ if (nextPage) {
+ resp = await shopifyFetch(nextPage.url)
+ const { events: nextEvents } = await resp.json()
+ events.push(...nextEvents)
+ } else {
+ gatherPaginatedEvents = false
+ break
+ }
+ }
+
+ return events
+ },
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/gatsby-node.js b/packages/gatsby-source-shopify/src/gatsby-node.js
deleted file mode 100644
index 127143d4777cd..0000000000000
--- a/packages/gatsby-source-shopify/src/gatsby-node.js
+++ /dev/null
@@ -1,275 +0,0 @@
-import { pipe } from "lodash/fp"
-import chalk from "chalk"
-import { forEach } from "p-iteration"
-import { printGraphQLError, queryAll, queryOnce } from "./lib"
-import { createClient } from "./create-client"
-
-import {
- ArticleNode,
- BlogNode,
- CollectionNode,
- CommentNode,
- ProductNode,
- ProductOptionNode,
- ProductVariantNode,
- ProductMetafieldNode,
- ProductVariantMetafieldNode,
- ShopPolicyNode,
- ShopDetailsNode,
- PageNode,
-} from "./nodes"
-import {
- SHOP,
- CONTENT,
- NODE_TO_ENDPOINT_MAPPING,
- ARTICLE,
- BLOG,
- COLLECTION,
- PRODUCT,
- SHOP_POLICY,
- SHOP_DETAILS,
- PAGE,
-} from "./constants"
-import {
- ARTICLES_QUERY,
- BLOGS_QUERY,
- COLLECTIONS_QUERY,
- PRODUCTS_QUERY,
- SHOP_POLICIES_QUERY,
- SHOP_DETAILS_QUERY,
- PAGES_QUERY,
-} from "./queries"
-
-export const sourceNodes = async (
- {
- actions: { createNode, touchNode },
- createNodeId,
- store,
- cache,
- getCache,
- reporter,
- getNode,
- },
- {
- shopName,
- accessToken,
- apiVersion = `2020-07`,
- verbose = true,
- paginationSize = 250,
- includeCollections = [SHOP, CONTENT],
- downloadImages = true,
- shopifyQueries = {},
- }
-) => {
- const client = createClient(shopName, accessToken, apiVersion)
-
- const defaultQueries = {
- articles: ARTICLES_QUERY,
- blogs: BLOGS_QUERY,
- collections: COLLECTIONS_QUERY,
- products: PRODUCTS_QUERY,
- shopPolicies: SHOP_POLICIES_QUERY,
- shopDetails: SHOP_DETAILS_QUERY,
- pages: PAGES_QUERY,
- }
-
- const queries = { ...defaultQueries, ...shopifyQueries }
-
- // Convenience function to namespace console messages.
- const formatMsg = msg =>
- chalk`\n{blue gatsby-source-shopify/${shopName}} ${msg}`
-
- try {
- console.log(formatMsg(`starting to fetch data from Shopify`))
-
- // Arguments used for file node creation.
- const imageArgs = {
- createNode,
- createNodeId,
- touchNode,
- store,
- cache,
- getCache,
- getNode,
- reporter,
- downloadImages,
- }
-
- // Arguments used for node creation.
- const args = {
- client,
- createNode,
- createNodeId,
- formatMsg,
- verbose,
- imageArgs,
- paginationSize,
- queries,
- }
-
- // Message printed when fetching is complete.
- const msg = formatMsg(`finished fetching data from Shopify`)
-
- let promises = []
- if (includeCollections.includes(SHOP)) {
- promises = promises.concat([
- createNodes(COLLECTION, queries.collections, CollectionNode, args),
- createNodes(
- PRODUCT,
- queries.products,
- ProductNode,
- args,
- async (product, productNode) => {
- if (product.variants)
- await forEach(product.variants.edges, async edge => {
- const v = edge.node
- if (v.metafields)
- await forEach(v.metafields.edges, async edge =>
- createNode(
- await ProductVariantMetafieldNode(imageArgs)(edge.node)
- )
- )
- return createNode(
- await ProductVariantNode(imageArgs, productNode)(edge.node)
- )
- })
-
- if (product.metafields)
- await forEach(product.metafields.edges, async edge =>
- createNode(await ProductMetafieldNode(imageArgs)(edge.node))
- )
-
- if (product.options)
- await forEach(product.options, async option =>
- createNode(await ProductOptionNode(imageArgs)(option))
- )
- }
- ),
- createShopPolicies(args),
- createShopDetails(args),
- ])
- }
- if (includeCollections.includes(CONTENT)) {
- promises = promises.concat([
- createNodes(BLOG, queries.blogs, BlogNode, args),
- createNodes(ARTICLE, queries.articles, ArticleNode, args, async x => {
- if (x.comments)
- await forEach(x.comments.edges, async edge =>
- createNode(await CommentNode(imageArgs)(edge.node))
- )
- }),
- createPageNodes(PAGE, queries.pages, PageNode, args),
- ])
- }
-
- console.time(msg)
- await Promise.all(promises)
- console.timeEnd(msg)
- } catch (e) {
- console.error(chalk`\n{red error} an error occurred while sourcing data`)
-
- // If not a GraphQL request error, let Gatsby print the error.
- if (!e.hasOwnProperty(`request`)) throw e
-
- printGraphQLError(e)
- }
-}
-
-/**
- * Fetch and create nodes for the provided endpoint, query, and node factory.
- */
-const createNodes = async (
- endpoint,
- query,
- nodeFactory,
- { client, createNode, formatMsg, verbose, imageArgs, paginationSize },
- f = async () => {}
-) => {
- // Message printed when fetching is complete.
- const msg = formatMsg(`fetched and processed ${endpoint} nodes`)
-
- if (verbose) console.time(msg)
- await forEach(
- await queryAll(
- client,
- [NODE_TO_ENDPOINT_MAPPING[endpoint]],
- query,
- paginationSize
- ),
- async entity => {
- const node = await nodeFactory(imageArgs)(entity)
- createNode(node)
- await f(entity, node)
- }
- )
- if (verbose) console.timeEnd(msg)
-}
-
-/**
- * Fetch and create nodes for shop policies.
- */
-const createShopDetails = async ({
- client,
- createNode,
- formatMsg,
- verbose,
- queries,
-}) => {
- // // Message printed when fetching is complete.
- const msg = formatMsg(`fetched and processed ${SHOP_DETAILS} nodes`)
-
- if (verbose) console.time(msg)
- const { shop } = await queryOnce(client, queries.shopDetails)
- createNode(ShopDetailsNode(shop))
- if (verbose) console.timeEnd(msg)
-}
-
-/**
- * Fetch and create nodes for shop policies.
- */
-const createShopPolicies = async ({
- client,
- createNode,
- formatMsg,
- verbose,
- queries,
-}) => {
- // Message printed when fetching is complete.
- const msg = formatMsg(`fetched and processed ${SHOP_POLICY} nodes`)
-
- if (verbose) console.time(msg)
- const { shop: policies } = await queryOnce(client, queries.shopPolicies)
- Object.entries(policies)
- .filter(([_, policy]) => Boolean(policy))
- .forEach(
- pipe(([type, policy]) => ShopPolicyNode(policy, { type }), createNode)
- )
- if (verbose) console.timeEnd(msg)
-}
-
-const createPageNodes = async (
- endpoint,
- query,
- nodeFactory,
- { client, createNode, formatMsg, verbose, paginationSize },
- f = async () => {}
-) => {
- // Message printed when fetching is complete.
- const msg = formatMsg(`fetched and processed ${endpoint} nodes`)
-
- if (verbose) console.time(msg)
- await forEach(
- await queryAll(
- client,
- [NODE_TO_ENDPOINT_MAPPING[endpoint]],
- query,
- paginationSize
- ),
- async entity => {
- const node = await nodeFactory(entity)
- createNode(node)
- await f(entity)
- }
- )
- if (verbose) console.timeEnd(msg)
-}
diff --git a/packages/gatsby-source-shopify/src/gatsby-node.ts b/packages/gatsby-source-shopify/src/gatsby-node.ts
new file mode 100644
index 0000000000000..cd71b6233364f
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/gatsby-node.ts
@@ -0,0 +1,292 @@
+import { createOperations } from "./operations"
+import { eventsApi } from "./events"
+import {
+ CreateResolversArgs,
+ NodePluginArgs,
+ PluginOptionsSchemaArgs,
+ SourceNodesArgs,
+} from "gatsby"
+import { makeResolveGatsbyImageData } from "./resolve-gatsby-image-data"
+import {
+ getGatsbyImageResolver,
+ IGatsbyGraphQLResolverArgumentConfig,
+} from "gatsby-plugin-image/graphql-utils"
+import { shiftLeft } from "shift-left"
+import { pluginErrorCodes as errorCodes } from "./errors"
+import { makeSourceFromOperation } from "./make-source-from-operation"
+export { createSchemaCustomization } from "./create-schema-customization"
+import { createNodeId } from "./node-builder"
+import { JoiObject } from "joi"
+
+export function pluginOptionsSchema({
+ Joi,
+}: PluginOptionsSchemaArgs): JoiObject {
+ // @ts-ignore TODO: When Gatsby updates Joi version, update type
+ // Vague type error that we're not able to figure out related to isJoi missing
+ // Probably related to Joi being outdated
+ return Joi.object({
+ apiKey: Joi.string().required(),
+ password: Joi.string().required(),
+ storeUrl: Joi.string()
+ .pattern(/^[a-z-]+\.myshopify\.com$/)
+ .message(
+ `The storeUrl value should be your store's myshopify.com URL in the form "my-site.myshopify.com", without https or slashes`
+ )
+ .required(),
+ downloadImages: Joi.boolean(),
+ typePrefix: Joi.string()
+ .pattern(new RegExp(`(^[A-Z]w*)`))
+ .message(
+ `"typePrefix" can only be alphanumeric characters, starting with an uppercase letter`
+ )
+ .default(``),
+ shopifyConnections: Joi.array()
+ .default([])
+ .items(Joi.string().valid(`orders`, `collections`)),
+ salesChannel: Joi.string(),
+ })
+}
+
+async function sourceAllNodes(
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+): Promise {
+ const {
+ createProductsOperation,
+ createOrdersOperation,
+ createCollectionsOperation,
+ finishLastOperation,
+ completedOperation,
+ cancelOperationInProgress,
+ } = createOperations(pluginOptions, gatsbyApi)
+
+ const operations = [createProductsOperation]
+ if (pluginOptions.shopifyConnections?.includes(`orders`)) {
+ operations.push(createOrdersOperation)
+ }
+
+ if (pluginOptions.shopifyConnections?.includes(`collections`)) {
+ operations.push(createCollectionsOperation)
+ }
+
+ const sourceFromOperation = makeSourceFromOperation(
+ finishLastOperation,
+ completedOperation,
+ cancelOperationInProgress,
+ gatsbyApi,
+ pluginOptions
+ )
+
+ for (const op of operations) {
+ await sourceFromOperation(op)
+ }
+}
+
+const shopifyNodeTypes = [
+ `ShopifyLineItem`,
+ `ShopifyProductMetafield`,
+ `ShopifyProductVariantMetafield`,
+ `ShopifyCollectionMetafield`,
+ `ShopifyOrder`,
+ `ShopifyProduct`,
+ `ShopifyCollection`,
+ `ShopifyProductImage`,
+ `ShopifyCollectionImage`,
+ `ShopifyProductFeaturedImage`,
+ `ShopifyProductVariant`,
+ `ShopifyProductVariantImage`,
+ `ShopifyProductVariantPricePair`,
+ `ShopifyProductFeaturedMediaPreviewImage`,
+]
+
+async function sourceChangedNodes(
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+): Promise {
+ const {
+ incrementalProducts,
+ incrementalOrders,
+ incrementalCollections,
+ finishLastOperation,
+ completedOperation,
+ cancelOperationInProgress,
+ } = createOperations(pluginOptions, gatsbyApi)
+ const { typePrefix = `` } = pluginOptions
+ const lastBuildTime = new Date(
+ gatsbyApi.store.getState().status.plugins?.[`gatsby-source-shopify`]?.[
+ `lastBuildTime${typePrefix}`
+ ]
+ )
+
+ for (const nodeType of shopifyNodeTypes) {
+ gatsbyApi
+ .getNodesByType(`${typePrefix}${nodeType}`)
+ .forEach(node => gatsbyApi.actions.touchNode(node))
+ }
+
+ const operations = [incrementalProducts(lastBuildTime)]
+ if (pluginOptions.shopifyConnections?.includes(`orders`)) {
+ operations.push(incrementalOrders(lastBuildTime))
+ }
+
+ if (pluginOptions.shopifyConnections?.includes(`collections`)) {
+ operations.push(incrementalCollections(lastBuildTime))
+ }
+
+ const sourceFromOperation = makeSourceFromOperation(
+ finishLastOperation,
+ completedOperation,
+ cancelOperationInProgress,
+ gatsbyApi,
+ pluginOptions
+ )
+
+ for (const op of operations) {
+ await sourceFromOperation(op)
+ }
+
+ const { fetchDestroyEventsSince } = eventsApi(pluginOptions)
+ const destroyEvents = await fetchDestroyEventsSince(lastBuildTime)
+
+ gatsbyApi.reporter.info(
+ `${destroyEvents.length} items have been deleted since ${lastBuildTime}`
+ )
+
+ if (destroyEvents.length) {
+ gatsbyApi.reporter.info(`Removing matching nodes from Gatsby`)
+ destroyEvents.forEach(e => {
+ const id = `${typePrefix}gid://shopify/${e.subject_type}/${e.subject_id}`
+ gatsbyApi.reporter.info(`Looking up node with ID: ${id}`)
+ const nodeId = createNodeId(id, gatsbyApi, pluginOptions)
+ const node = gatsbyApi.getNode(nodeId)
+
+ if (node) {
+ gatsbyApi.reporter.info(
+ `Removing ${node.internal.type}: ${node.id} with shopifyId ${e.subject_id}`
+ )
+ gatsbyApi.actions.deleteNode(node)
+ } else {
+ gatsbyApi.reporter.info(`Couldn't find node with ID: ${id}`)
+ }
+ })
+ }
+}
+
+export async function sourceNodes(
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+): Promise {
+ const pluginStatus = gatsbyApi.store.getState().status.plugins?.[
+ `gatsby-source-shopify`
+ ]
+
+ const lastBuildTime =
+ pluginStatus?.[`lastBuildTime${pluginOptions.typePrefix || ``}`]
+
+ if (lastBuildTime !== undefined) {
+ gatsbyApi.reporter.info(`Cache is warm, running an incremental build`)
+ await sourceChangedNodes(gatsbyApi, pluginOptions)
+ } else {
+ gatsbyApi.reporter.info(`Cache is cold, running a clean build`)
+ await sourceAllNodes(gatsbyApi, pluginOptions)
+ }
+
+ gatsbyApi.reporter.info(`Finished sourcing nodes, caching last build time`)
+ gatsbyApi.actions.setPluginStatus(
+ pluginStatus !== undefined
+ ? {
+ ...pluginStatus,
+ [`lastBuildTime${pluginOptions.typePrefix || ``}`]: Date.now(),
+ }
+ : {
+ [`lastBuildTime${pluginOptions.typePrefix || ``}`]: Date.now(),
+ }
+ )
+}
+
+export function createResolvers(
+ { createResolvers, cache }: CreateResolversArgs,
+ {
+ downloadImages,
+ typePrefix = ``,
+ shopifyConnections = [],
+ }: ShopifyPluginOptions
+): void {
+ if (!downloadImages) {
+ const args = {
+ placeholder: {
+ description: `Low resolution version of the image`,
+ type: `String`,
+ defaultValue: null,
+ } as IGatsbyGraphQLResolverArgumentConfig,
+ }
+ const imageNodeTypes = [
+ `ShopifyProductImage`,
+ `ShopifyProductVariantImage`,
+ `ShopifyProductFeaturedImage`,
+ `ShopifyProductFeaturedMediaPreviewImage`,
+ ]
+
+ if (shopifyConnections.includes(`collections`)) {
+ imageNodeTypes.push(`ShopifyCollectionImage`)
+ }
+
+ const resolvers = imageNodeTypes.reduce((r, nodeType) => {
+ return {
+ ...r,
+ [`${typePrefix}${nodeType}`]: {
+ gatsbyImageData: getGatsbyImageResolver(
+ makeResolveGatsbyImageData(cache),
+ args
+ ),
+ },
+ }
+ }, {})
+
+ createResolvers(resolvers)
+ }
+}
+
+interface IErrorContext {
+ sourceMessage: string
+}
+
+const getErrorText = (context: IErrorContext): string => context.sourceMessage
+
+export function onPreInit({ reporter }: NodePluginArgs): void {
+ reporter.setErrorMap({
+ [errorCodes.bulkOperationFailed]: {
+ text: getErrorText,
+ level: `ERROR`,
+ category: `USER`,
+ },
+ [errorCodes.apiConflict]: {
+ text: (): string => shiftLeft`
+ Your operation was canceled. You might have another production site for this Shopify store.
+
+ Shopify only allows one bulk operation at a time for a given shop, so we recommend that you
+ avoid having two production sites that point to the same Shopify store.
+
+ If the duplication is intentional, please wait for the other operation to finish before trying
+ again. Otherwise, consider deleting the other site or pointing it to a test store instead.
+ `,
+ level: `ERROR`,
+ category: `USER`,
+ },
+ /**
+ * If we don't know what it is, we haven't done our due
+ * diligence to handle it explicitly. That means it's our
+ * fault, so THIRD_PARTY indicates us, the plugin authors.
+ */
+ [errorCodes.unknownSourcingFailure]: {
+ text: getErrorText,
+ level: `ERROR`,
+ category: `THIRD_PARTY`,
+ },
+ [errorCodes.unknownApiError]: {
+ text: getErrorText,
+ level: `ERROR`,
+ category: `THIRD_PARTY`,
+ },
+ })
+}
diff --git a/packages/gatsby-source-shopify/src/get-shopify-image.ts b/packages/gatsby-source-shopify/src/get-shopify-image.ts
new file mode 100644
index 0000000000000..de78b865fdcb4
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/get-shopify-image.ts
@@ -0,0 +1,72 @@
+import {
+ IUrlBuilderArgs,
+ IGetImageDataArgs,
+ getImageData,
+ IGatsbyImageData,
+} from "gatsby-plugin-image"
+const validFormats = new Set([`jpg`, `png`, `webp`, `auto`])
+
+export interface IShopifyImage {
+ width: number
+ height: number
+ originalSrc: string
+}
+export interface IGetShopifyImageArgs
+ extends Omit<
+ IGetImageDataArgs,
+ "urlBuilder" | "baseUrl" | "formats" | "sourceWidth" | "sourceHeight"
+ > {
+ image: IShopifyImage
+}
+export function urlBuilder({
+ width,
+ height,
+ baseUrl,
+ format,
+}: IUrlBuilderArgs): string {
+ if (!validFormats.has(format)) {
+ console.warn(
+ `${format} is not a valid format. Valid formats are: ${[
+ ...validFormats,
+ ].join(`, `)}`
+ )
+ format = `auto`
+ }
+
+ let [basename, version] = baseUrl.split(`?`)
+
+ const dot = basename.lastIndexOf(`.`)
+ let ext = ``
+ if (dot !== -1) {
+ ext = basename.slice(dot + 1)
+ basename = basename.slice(0, dot)
+ }
+ let suffix = ``
+ if (format === ext || format === `auto`) {
+ suffix = `.${ext}`
+ } else {
+ suffix = `.${ext}.${format}`
+ }
+
+ return `${basename}_${width}x${height}_crop_center${suffix}?${version}`
+}
+
+export function getShopifyImage({
+ image,
+ ...args
+}: IGetShopifyImageArgs): IGatsbyImageData {
+ const {
+ originalSrc: baseUrl,
+ width: sourceWidth,
+ height: sourceHeight,
+ } = image
+
+ return getImageData({
+ ...args,
+ baseUrl,
+ sourceWidth,
+ sourceHeight,
+ urlBuilder,
+ formats: [`auto`],
+ })
+}
diff --git a/packages/gatsby-source-shopify/src/index.js b/packages/gatsby-source-shopify/src/index.js
deleted file mode 100644
index 23fdb69814b6d..0000000000000
--- a/packages/gatsby-source-shopify/src/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./constants"
diff --git a/packages/gatsby-source-shopify/src/index.ts b/packages/gatsby-source-shopify/src/index.ts
new file mode 100644
index 0000000000000..e6f10954e72f6
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/index.ts
@@ -0,0 +1 @@
+export { getShopifyImage } from "./get-shopify-image"
diff --git a/packages/gatsby-source-shopify/src/lib.js b/packages/gatsby-source-shopify/src/lib.js
deleted file mode 100644
index 98520225bb5c3..0000000000000
--- a/packages/gatsby-source-shopify/src/lib.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import prettyjson from "prettyjson"
-import chalk from "chalk"
-import { get, getOr, last } from "lodash/fp"
-
-/**
- * Print an error from a GraphQL client
- */
-export const printGraphQLError = e => {
- const prettyjsonOptions = { keysColor: `red`, dashColor: `red` }
-
- if (e.response && e.response.errors) {
- if (e.message.startsWith(`access denied`)) {
- console.error(chalk`\n{yellow Check your token has this read authorization,
- or omit fetching this object using the "includeCollections" options in gatsby-source-shopify plugin options}`)
- }
- console.error(prettyjson.render(e.response.errors, prettyjsonOptions))
- }
-
- if (e.request) console.error(prettyjson.render(e.request, prettyjsonOptions))
-}
-
-/**
- * Request a query from a client.
- */
-export const queryOnce = async (client, query, first = 250, after) =>
- await client.request(query, { first, after })
-
-/**
- * Get all paginated data from a query. Will execute multiple requests as
- * needed.
- */
-export const queryAll = async (
- client,
- path,
- query,
- first = 250,
- after = null,
- aggregatedResponse = null
-) => {
- const data = await queryOnce(client, query, first, after)
- const edges = getOr([], [...path, `edges`], data)
- const nodes = edges.map(edge => edge.node)
-
- aggregatedResponse = aggregatedResponse
- ? aggregatedResponse.concat(nodes)
- : nodes
-
- if (get([...path, `pageInfo`, `hasNextPage`], data)) {
- return queryAll(
- client,
- path,
- query,
- first,
- last(edges).cursor,
- aggregatedResponse
- )
- }
-
- return aggregatedResponse
-}
diff --git a/packages/gatsby-source-shopify/src/make-source-from-operation.ts b/packages/gatsby-source-shopify/src/make-source-from-operation.ts
new file mode 100644
index 0000000000000..35623de3c60d3
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/make-source-from-operation.ts
@@ -0,0 +1,178 @@
+import fetch from "node-fetch"
+import { SourceNodesArgs } from "gatsby"
+import { createInterface } from "readline"
+import { shiftLeft } from "shift-left"
+
+import { nodeBuilder } from "./node-builder"
+import { IShopifyBulkOperation } from "./operations"
+import {
+ OperationError,
+ HttpError,
+ pluginErrorCodes as errorCodes,
+} from "./errors"
+
+export function makeSourceFromOperation(
+ finishLastOperation: () => Promise,
+ completedOperation: (id: string) => Promise<{ node: BulkOperationNode }>,
+ cancelOperationInProgress: () => Promise,
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+) {
+ return async function sourceFromOperation(
+ op: IShopifyBulkOperation,
+ isPriorityBuild = process.env.IS_PRODUCTION_BRANCH === `true`
+ ): Promise {
+ const { reporter, actions } = gatsbyApi
+
+ const operationTimer = reporter.activityTimer(
+ `Source from bulk operation ${op.name}`
+ )
+
+ operationTimer.start()
+
+ try {
+ if (isPriorityBuild) {
+ await cancelOperationInProgress()
+ } else {
+ await finishLastOperation()
+ }
+
+ reporter.info(`Initiating bulk operation query ${op.name}`)
+ const {
+ bulkOperationRunQuery: { userErrors, bulkOperation },
+ } = await op.execute()
+
+ if (userErrors.length) {
+ reporter.panic(
+ userErrors.map(e => {
+ const msg = e.field
+ ? `${e.field.join(`.`)}: ${e.message}`
+ : e.message
+
+ return {
+ id: errorCodes.bulkOperationFailed,
+ context: {
+ sourceMessage: `Couldn't initiate bulk operation query`,
+ },
+ error: new Error(msg),
+ }
+ })
+ )
+ }
+
+ const resp = await completedOperation(bulkOperation.id)
+ reporter.info(`Completed bulk operation ${op.name}: ${bulkOperation.id}`)
+
+ if (parseInt(resp.node.objectCount, 10) === 0) {
+ reporter.info(`No data was returned for this operation`)
+ operationTimer.end()
+ return
+ }
+
+ operationTimer.setStatus(
+ `Fetching ${resp.node.objectCount} results for ${op.name}: ${bulkOperation.id}`
+ )
+
+ const results = await fetch(resp.node.url)
+
+ operationTimer.setStatus(
+ `Processing ${resp.node.objectCount} results for ${op.name}: ${bulkOperation.id}`
+ )
+ const rl = createInterface({
+ input: results.body,
+ crlfDelay: Infinity,
+ })
+
+ reporter.info(`Creating nodes from bulk operation ${op.name}`)
+
+ const objects: BulkResults = []
+
+ for await (const line of rl) {
+ objects.push(JSON.parse(line))
+ }
+
+ await Promise.all(
+ op
+ .process(
+ objects,
+ nodeBuilder(gatsbyApi, pluginOptions),
+ gatsbyApi,
+ pluginOptions
+ )
+ .map(async promise => {
+ const node = await promise
+ actions.createNode(node)
+ })
+ )
+
+ operationTimer.end()
+ } catch (e) {
+ if (e instanceof OperationError) {
+ const code = errorCodes.bulkOperationFailed
+
+ if (e.node.status === `CANCELED`) {
+ if (isPriorityBuild) {
+ /**
+ * There are at least two production sites for this Shopify
+ * store trying to run an operation at the same time.
+ */
+ reporter.panic({
+ id: errorCodes.apiConflict,
+ error: e,
+ context: {},
+ })
+ } else {
+ // A prod build canceled me, wait and try again
+ operationTimer.setStatus(
+ `This operation has been canceled by a higher priority build. It will retry shortly.`
+ )
+ operationTimer.end()
+ await new Promise(resolve => setTimeout(resolve, 5000))
+ await sourceFromOperation(op)
+ }
+ }
+
+ if (e.node.errorCode === `ACCESS_DENIED`) {
+ reporter.panic({
+ id: code,
+ context: {
+ sourceMessage: `Your credentials don't have access to a resource you requested`,
+ },
+ error: e,
+ })
+ }
+
+ reporter.panic({
+ id: errorCodes.unknownSourcingFailure,
+ context: {
+ sourceMessage: shiftLeft`
+ Operation ${op.name} failed after ${e.node.objectCount} objects
+ - With status: ${e.node.status} - error code: ${e.node.errorCode}
+ `,
+ },
+ error: e,
+ })
+ }
+
+ if (e instanceof HttpError) {
+ reporter.panic({
+ id: errorCodes.unknownApiError,
+ context: {
+ sourceMessage: `Received error ${
+ e.response.status
+ } from Shopify: ${await e.response.text()}`,
+ },
+ error: e,
+ })
+ }
+
+ reporter.panic({
+ id: errorCodes.unknownSourcingFailure,
+ context: {
+ sourceMessage: `Could not source from bulk operation`,
+ },
+ error: e,
+ })
+ }
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/node-builder.ts b/packages/gatsby-source-shopify/src/node-builder.ts
new file mode 100644
index 0000000000000..fb809b40fd015
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/node-builder.ts
@@ -0,0 +1,194 @@
+import { NodeInput, SourceNodesArgs } from "gatsby"
+import { createRemoteFileNode } from "gatsby-source-filesystem"
+
+// 'gid://shopify/Metafield/6936247730264'
+export const pattern = /^gid:\/\/shopify\/(\w+)\/(.+)$/
+
+export function createNodeId(
+ shopifyId: string,
+ gatsbyApi: SourceNodesArgs,
+ { typePrefix = `` }: ShopifyPluginOptions
+): string {
+ return gatsbyApi.createNodeId(`${typePrefix}${shopifyId}`)
+}
+
+function attachParentId(
+ result: BulkResult,
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+): void {
+ if (result.__parentId) {
+ const [fullId, remoteType] = result.__parentId.match(pattern) || []
+ const field = remoteType.charAt(0).toLowerCase() + remoteType.slice(1)
+ const idField = `${field}Id`
+ result[idField] = createNodeId(fullId, gatsbyApi, pluginOptions)
+ }
+}
+
+const downloadImageAndCreateFileNode = async (
+ { url, nodeId }: { url: string; nodeId: string },
+ {
+ actions: { createNode },
+ createNodeId,
+ cache,
+ store,
+ reporter,
+ }: SourceNodesArgs
+): Promise => {
+ const fileNode = await createRemoteFileNode({
+ url,
+ cache,
+ createNode,
+ createNodeId,
+ parentNodeId: nodeId,
+ store,
+ reporter,
+ })
+
+ return fileNode.id
+}
+
+interface IProcessorMap {
+ [remoteType: string]: (
+ node: NodeInput,
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+ ) => Promise
+}
+
+interface IImageData {
+ id: string
+ originalSrc: string
+ localFile: string | undefined
+}
+
+async function processChildImage(
+ node: NodeInput,
+ getImageData: (node: NodeInput) => IImageData | undefined,
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+): Promise {
+ if (pluginOptions.downloadImages) {
+ const image = getImageData(node)
+
+ if (image) {
+ const url = image.originalSrc
+ const fileNodeId = await downloadImageAndCreateFileNode(
+ {
+ url,
+ nodeId: node.id,
+ },
+ gatsbyApi
+ )
+
+ image.localFile = fileNodeId
+ }
+ }
+}
+
+export const processorMap: IProcessorMap = {
+ LineItem: async (node, gatsbyApi, pluginOptions) => {
+ const lineItem = node
+ if (lineItem.product) {
+ lineItem.productId = createNodeId(
+ (lineItem.product as BulkResult).id,
+ gatsbyApi,
+ pluginOptions
+ )
+ delete lineItem.product
+ }
+ },
+ ProductImage: async (node, gatsbyApi, options) => {
+ if (options.downloadImages) {
+ const url = node.originalSrc as string
+ const fileNodeId = await downloadImageAndCreateFileNode(
+ {
+ url,
+ nodeId: node.id,
+ },
+ gatsbyApi
+ )
+
+ node.localFile = fileNodeId
+ }
+ },
+ Collection: async (node, gatsbyApi, options) =>
+ processChildImage(
+ node,
+ node => node.image as IImageData,
+ gatsbyApi,
+ options
+ ),
+ Product: async (node, gatsbyApi, options) => {
+ await processChildImage(
+ node,
+ node => node.featuredImage as IImageData,
+ gatsbyApi,
+ options
+ )
+ await processChildImage(
+ node,
+ node => {
+ const media = node.featuredMedia as
+ | {
+ preview?: {
+ image?: IImageData
+ }
+ }
+ | undefined
+
+ return media?.preview?.image
+ },
+ gatsbyApi,
+ options
+ )
+ },
+ ProductVariant: async (node, gatsbyApi, options) =>
+ processChildImage(
+ node,
+ node => node.image as IImageData,
+ gatsbyApi,
+ options
+ ),
+ Metafield: async (node, _gatsbyApi, { typePrefix = `` }) => {
+ const [, parentType] = (node.__parentId as string).match(pattern) || []
+ const internalType = `${typePrefix}Shopify${parentType}Metafield`
+ node.internal.type = internalType
+ },
+}
+
+export function nodeBuilder(
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+): NodeBuilder {
+ return {
+ async buildNode(result: BulkResult): Promise {
+ if (!pattern.test(result.id)) {
+ throw new Error(
+ `Expected an ID in the format gid://shopify//`
+ )
+ }
+
+ const [, remoteType] = result.id.match(pattern) || []
+
+ const processor =
+ processorMap[remoteType] || ((): Promise => Promise.resolve())
+
+ attachParentId(result, gatsbyApi, pluginOptions)
+
+ const node = {
+ ...result,
+ shopifyId: result.id,
+ id: createNodeId(result.id, gatsbyApi, pluginOptions),
+ internal: {
+ type: `${pluginOptions.typePrefix || ``}Shopify${remoteType}`,
+ contentDigest: gatsbyApi.createContentDigest(result),
+ },
+ }
+
+ await processor(node, gatsbyApi, pluginOptions)
+
+ return node
+ },
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/nodes.js b/packages/gatsby-source-shopify/src/nodes.js
deleted file mode 100644
index e57f998dfded3..0000000000000
--- a/packages/gatsby-source-shopify/src/nodes.js
+++ /dev/null
@@ -1,195 +0,0 @@
-import createNodeHelpers from "gatsby-node-helpers"
-import { map } from "p-iteration"
-import { createRemoteFileNode } from "gatsby-source-filesystem"
-
-import {
- TYPE_PREFIX,
- ARTICLE,
- BLOG,
- COLLECTION,
- COMMENT,
- PRODUCT,
- PRODUCT_OPTION,
- PRODUCT_VARIANT,
- PRODUCT_METAFIELD,
- PRODUCT_VARIANT_METAFIELD,
- SHOP_POLICY,
- SHOP_DETAILS,
- PAGE,
-} from "./constants"
-
-const { createNodeFactory, generateNodeId } = createNodeHelpers({
- typePrefix: TYPE_PREFIX,
-})
-
-const downloadImageAndCreateFileNode = async (
- { url, nodeId },
- {
- createNode,
- createNodeId,
- touchNode,
- store,
- cache,
- getCache,
- getNode,
- reporter,
- downloadImages,
- }
-) => {
- if (!downloadImages) return undefined
-
- const mediaDataCacheKey = `${TYPE_PREFIX}__Media__${url}`
- const cacheMediaData = await cache.get(mediaDataCacheKey)
-
- if (cacheMediaData) {
- const fileNodeID = cacheMediaData.fileNodeID
- touchNode(getNode(fileNodeID))
- return fileNodeID
- }
-
- const fileNode = await createRemoteFileNode({
- url,
- store,
- cache,
- createNode,
- createNodeId,
- getCache,
- parentNodeId: nodeId,
- reporter,
- })
-
- if (fileNode) {
- const fileNodeID = fileNode.id
- await cache.set(mediaDataCacheKey, { fileNodeID })
- return fileNodeID
- }
-
- return undefined
-}
-
-export const ArticleNode = imageArgs =>
- createNodeFactory(ARTICLE, async node => {
- if (node.blog) node.blog___NODE = generateNodeId(BLOG, node.blog.id)
-
- if (node.comments)
- node.comments___NODE = node.comments.edges.map(edge =>
- generateNodeId(COMMENT, edge.node.id)
- )
-
- if (node.image)
- node.image.localFile___NODE = await downloadImageAndCreateFileNode(
- { id: node.image.id, url: node.image.src, nodeId: node.id },
- imageArgs
- )
-
- return node
- })
-
-export const BlogNode = _imageArgs => createNodeFactory(BLOG)
-
-export const CollectionNode = imageArgs =>
- createNodeFactory(COLLECTION, async node => {
- if (node.products) {
- node.products___NODE = node.products.edges.map(edge =>
- generateNodeId(PRODUCT, edge.node.id)
- )
- delete node.products
- }
- if (node.image)
- node.image.localFile___NODE = await downloadImageAndCreateFileNode(
- {
- id: node.image.id,
- url: node.image.src,
- nodeId: node.id,
- },
- imageArgs
- )
- return node
- })
-
-export const CommentNode = _imageArgs => createNodeFactory(COMMENT)
-
-export const ProductNode = imageArgs =>
- createNodeFactory(PRODUCT, async node => {
- if (node.variants) {
- const variants = node.variants.edges.map(edge => edge.node)
-
- node.variants___NODE = variants.map(variant =>
- generateNodeId(PRODUCT_VARIANT, variant.id)
- )
-
- delete node.variants
- }
-
- if (node.metafields) {
- const metafields = node.metafields.edges.map(edge => edge.node)
-
- node.metafields___NODE = metafields.map(metafield =>
- generateNodeId(PRODUCT_METAFIELD, metafield.id)
- )
- delete node.metafields
- }
-
- if (node.options) {
- node.options___NODE = node.options.map(option =>
- generateNodeId(PRODUCT_OPTION, option.id)
- )
- delete node.options
- }
-
- if (node.images && node.images.edges)
- node.images = await map(node.images.edges, async edge => {
- edge.node.localFile___NODE = await downloadImageAndCreateFileNode(
- {
- id: edge.node.id,
- url: edge.node.originalSrc,
- },
- imageArgs
- )
- return edge.node
- })
-
- return node
- })
-
-export const ProductMetafieldNode = _imageArgs =>
- createNodeFactory(PRODUCT_METAFIELD)
-
-export const ProductOptionNode = _imageArgs => createNodeFactory(PRODUCT_OPTION)
-
-export const ProductVariantNode = (imageArgs, productNode) =>
- createNodeFactory(PRODUCT_VARIANT, async node => {
- if (node.metafields) {
- const metafields = node.metafields.edges.map(edge => edge.node)
-
- node.metafields___NODE = metafields.map(metafield =>
- generateNodeId(PRODUCT_VARIANT_METAFIELD, metafield.id)
- )
- delete node.metafields
- }
-
- if (node.image)
- node.image.localFile___NODE = await downloadImageAndCreateFileNode(
- {
- id: node.image.id,
- url: node.image.originalSrc,
- },
- imageArgs
- )
-
- if (!isNaN(node.price)) {
- node.priceNumber = parseFloat(node.price)
- }
-
- node.product___NODE = productNode.id
- return node
- })
-
-export const ProductVariantMetafieldNode = _imageArgs =>
- createNodeFactory(PRODUCT_VARIANT_METAFIELD)
-
-export const ShopPolicyNode = createNodeFactory(SHOP_POLICY)
-
-export const ShopDetailsNode = createNodeFactory(SHOP_DETAILS)
-
-export const PageNode = createNodeFactory(PAGE)
diff --git a/packages/gatsby-source-shopify/src/operations.ts b/packages/gatsby-source-shopify/src/operations.ts
new file mode 100644
index 0000000000000..e782fe433c2de
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/operations.ts
@@ -0,0 +1,255 @@
+import { NodeInput, SourceNodesArgs } from "gatsby"
+import { shiftLeft } from "shift-left"
+import { createClient } from "./client"
+import { ProductsQuery } from "./query-builders/products-query"
+import { CollectionsQuery } from "./query-builders/collections-query"
+import { OrdersQuery } from "./query-builders/orders-query"
+import {
+ collectionsProcessor,
+ incrementalProductsProcessor,
+} from "./processors"
+import { OperationError } from "./errors"
+
+import {
+ OPERATION_STATUS_QUERY,
+ OPERATION_BY_ID,
+ CANCEL_OPERATION,
+} from "./static-queries"
+
+export interface IShopifyBulkOperation {
+ execute: () => Promise
+ name: string
+ process: (
+ objects: BulkResults,
+ nodeBuilder: NodeBuilder,
+ _gatsbyApi: SourceNodesArgs,
+ _pluginOptions: ShopifyPluginOptions
+ ) => Array>
+}
+
+interface IOperations {
+ incrementalProducts: (date: Date) => IShopifyBulkOperation
+ incrementalOrders: (date: Date) => IShopifyBulkOperation
+ incrementalCollections: (date: Date) => IShopifyBulkOperation
+ createProductsOperation: IShopifyBulkOperation
+ createOrdersOperation: IShopifyBulkOperation
+ createCollectionsOperation: IShopifyBulkOperation
+ cancelOperationInProgress: () => Promise
+ cancelOperation: (id: string) => Promise
+ finishLastOperation: () => Promise
+ completedOperation: (
+ operationId: string,
+ interval?: number
+ ) => Promise<{ node: BulkOperationNode }>
+}
+
+const finishedStatuses = [`COMPLETED`, `FAILED`, `CANCELED`, `EXPIRED`]
+const failedStatuses = [`FAILED`, `CANCELED`]
+
+function defaultProcessor(
+ objects: BulkResults,
+ builder: NodeBuilder
+): Array> {
+ return objects.map(builder.buildNode)
+}
+
+export function createOperations(
+ options: ShopifyPluginOptions,
+ { reporter }: SourceNodesArgs
+): IOperations {
+ const client = createClient(options)
+
+ function currentOperation(): Promise {
+ return client.request(OPERATION_STATUS_QUERY)
+ }
+
+ function createOperation(
+ operationQuery: string,
+ name: string,
+ process?: IShopifyBulkOperation["process"]
+ ): IShopifyBulkOperation {
+ return {
+ execute: (): Promise =>
+ client.request(operationQuery),
+ name,
+ process: process || defaultProcessor,
+ }
+ }
+
+ async function finishLastOperation(): Promise {
+ let { currentBulkOperation } = await currentOperation()
+ if (currentBulkOperation && currentBulkOperation.id) {
+ const timer = reporter.activityTimer(
+ `Waiting for operation ${currentBulkOperation.id} : ${currentBulkOperation.status}`
+ )
+ timer.start()
+
+ while (!finishedStatuses.includes(currentBulkOperation.status)) {
+ await new Promise(resolve => setTimeout(resolve, 1000))
+ currentBulkOperation = (await currentOperation()).currentBulkOperation
+ timer.setStatus(
+ `Polling operation ${currentBulkOperation.id} : ${currentBulkOperation.status}`
+ )
+ }
+
+ timer.end()
+ }
+ }
+
+ async function cancelOperation(
+ id: string
+ ): Promise {
+ return client.request(CANCEL_OPERATION, {
+ id,
+ })
+ }
+
+ async function cancelOperationInProgress(): Promise {
+ let { currentBulkOperation: bulkOperation } = await currentOperation()
+ if (!bulkOperation) {
+ return
+ }
+
+ const cancelTimer = reporter.activityTimer(
+ `Canceling previous operation: ${bulkOperation.id}`
+ )
+
+ cancelTimer.start()
+
+ if (bulkOperation.status === `RUNNING`) {
+ cancelTimer.setStatus(
+ `Canceling a currently running operation: ${bulkOperation.id}, this could take a few moments`
+ )
+
+ const { bulkOperationCancel } = await cancelOperation(bulkOperation.id)
+
+ bulkOperation = bulkOperationCancel.bulkOperation
+
+ while (bulkOperation.status !== `CANCELED`) {
+ await new Promise(resolve => setTimeout(resolve, 100))
+ const currentOp = await currentOperation()
+ bulkOperation = currentOp.currentBulkOperation
+ cancelTimer.setStatus(
+ `Waiting for operation to cancel: ${bulkOperation.id}, ${bulkOperation.status}`
+ )
+ }
+ } else {
+ /**
+ * Just because it's not running doesn't mean it's done. For
+ * example, it could be CANCELING. We still have to wait for it
+ * to be officially finished before we start a new one.
+ */
+ while (!finishedStatuses.includes(bulkOperation.status)) {
+ await new Promise(resolve => setTimeout(resolve, 100))
+ bulkOperation = (await currentOperation()).currentBulkOperation
+ cancelTimer.setStatus(
+ `Waiting for operation to cancel: ${bulkOperation.id}, ${bulkOperation.status}`
+ )
+ }
+ }
+
+ cancelTimer.end()
+ }
+
+ /* Maybe the interval should be adjustable, because users
+ * with larger data sets could easily wait longer. We could
+ * perhaps detect that the interval being used is too small
+ * based on returned object counts and iteration counts, and
+ * surface feedback to the user suggesting that they increase
+ * the interval.
+ */
+ async function completedOperation(
+ operationId: string,
+ interval = 1000
+ ): Promise<{ node: BulkOperationNode }> {
+ let operation = await client.request<{
+ node: BulkOperationNode
+ }>(OPERATION_BY_ID, {
+ id: operationId,
+ })
+
+ const completedTimer = reporter.activityTimer(
+ `Waiting for bulk operation to complete`
+ )
+
+ completedTimer.start()
+
+ let waitForOperation = true
+
+ while (waitForOperation) {
+ if (failedStatuses.includes(operation.node.status)) {
+ completedTimer.end()
+ waitForOperation = false
+ throw new OperationError(operation.node)
+ }
+
+ if (operation.node.status === `COMPLETED`) {
+ completedTimer.end()
+ waitForOperation = false
+ return operation
+ }
+
+ await new Promise(resolve => setTimeout(resolve, interval))
+
+ operation = await client.request<{
+ node: BulkOperationNode
+ }>(OPERATION_BY_ID, {
+ id: operationId,
+ })
+
+ completedTimer.setStatus(shiftLeft`
+ Polling bulk operation: ${operation.node.id}
+ Status: ${operation.node.status}
+ Object count: ${operation.node.objectCount}
+ `)
+ }
+
+ throw new Error(`It should never reach this error`)
+ }
+
+ return {
+ incrementalProducts(date: Date): IShopifyBulkOperation {
+ return createOperation(
+ new ProductsQuery(options).query(date),
+ `INCREMENTAL_PRODUCTS`,
+ incrementalProductsProcessor
+ )
+ },
+
+ incrementalOrders(date: Date): IShopifyBulkOperation {
+ return createOperation(
+ new OrdersQuery(options).query(date),
+ `INCREMENTAL_ORDERS`
+ )
+ },
+
+ incrementalCollections(date: Date): IShopifyBulkOperation {
+ return createOperation(
+ new CollectionsQuery(options).query(date),
+ `INCREMENTAL_COLLECTIONS`,
+ collectionsProcessor
+ )
+ },
+
+ createProductsOperation: createOperation(
+ new ProductsQuery(options).query(),
+ `PRODUCTS`
+ ),
+
+ createOrdersOperation: createOperation(
+ new OrdersQuery(options).query(),
+ `ORDERS`
+ ),
+
+ createCollectionsOperation: createOperation(
+ new CollectionsQuery(options).query(),
+ `COLLECTIONS`,
+ collectionsProcessor
+ ),
+
+ cancelOperationInProgress,
+ cancelOperation,
+ finishLastOperation,
+ completedOperation,
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/processors/collections.ts b/packages/gatsby-source-shopify/src/processors/collections.ts
new file mode 100644
index 0000000000000..9747485c482a6
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/processors/collections.ts
@@ -0,0 +1,49 @@
+import { NodeInput, SourceNodesArgs } from "gatsby"
+import { pattern as idPattern, createNodeId } from "../node-builder"
+
+export function collectionsProcessor(
+ objects: BulkResults,
+ builder: NodeBuilder,
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+): Array> {
+ const promises = []
+ const collectionProductIndex: { [collectionId: string]: Array } = {}
+
+ /**
+ * Read results in reverse so we can collect child node IDs.
+ * See Shopify Bulk Operation guide for more info.
+ *
+ * https://shopify.dev/tutorials/perform-bulk-operations-with-admin-api#download-result-data
+ */
+ for (let i = objects.length - 1; i >= 0; i--) {
+ const result = objects[i]
+ const [id, remoteType] = result.id.match(idPattern) || []
+ if (remoteType === `Product`) {
+ /**
+ * We source products in a separate operation. Here we are
+ * just collecting product IDs so we can tell Gatsby about
+ * the many-to-many relationship between collections and
+ * products.
+ */
+ if (!collectionProductIndex[result.__parentId]) {
+ collectionProductIndex[result.__parentId] = []
+ }
+
+ collectionProductIndex[result.__parentId].unshift(
+ createNodeId(id, gatsbyApi, pluginOptions)
+ )
+ }
+
+ if (remoteType === `Metafield`) {
+ promises.push(builder.buildNode(result))
+ }
+
+ if (remoteType == `Collection`) {
+ result.productIds = collectionProductIndex[result.id] || []
+ promises.push(builder.buildNode(result))
+ }
+ }
+
+ return promises
+}
diff --git a/packages/gatsby-source-shopify/src/processors/incremental-products.ts b/packages/gatsby-source-shopify/src/processors/incremental-products.ts
new file mode 100644
index 0000000000000..c7719de918375
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/processors/incremental-products.ts
@@ -0,0 +1,58 @@
+import { NodeInput, SourceNodesArgs } from "gatsby"
+import { pattern as idPattern, createNodeId } from "../node-builder"
+
+export function incrementalProductsProcessor(
+ objects: BulkResults,
+ builder: NodeBuilder,
+ gatsbyApi: SourceNodesArgs,
+ pluginOptions: ShopifyPluginOptions
+): Array> {
+ const { typePrefix = `` } = pluginOptions
+ const products = objects.filter(obj => {
+ const [, remoteType] = obj.id.match(idPattern) || []
+
+ return remoteType === `Product`
+ })
+
+ const nodeIds = products.map(product =>
+ createNodeId(product.id, gatsbyApi, pluginOptions)
+ )
+
+ /**
+ * The events API doesn't tell us about deleted variants or images, so when we
+ * get the list of changed products, we have to compare those product
+ * variants/images with what we have in the cache, and delete those that are
+ * not present in the newer API results.
+ */
+ const variantsToDelete = gatsbyApi
+ .getNodesByType(`${typePrefix}ShopifyProductVariant`)
+ .filter(node => nodeIds.includes(node.productId as string))
+
+ variantsToDelete.forEach(variant => {
+ gatsbyApi.actions.deleteNode(variant)
+ })
+
+ const imagesToDelete = gatsbyApi
+ .getNodesByType(`${typePrefix}ShopifyProductImage`)
+ .filter(node => nodeIds.includes(node.productId as string))
+
+ imagesToDelete.forEach(image => {
+ gatsbyApi.actions.deleteNode(image)
+ })
+
+ /**
+ * Additionally, product variants have metafields attached to them, so
+ * we must delete those as well to avoid oprhaned nodes building up in
+ * the cache.
+ */
+ const variantIds = variantsToDelete.map(v => v.id)
+ gatsbyApi
+ .getNodesByType(`${typePrefix}ShopifyProductVariantMetafield`)
+ .forEach(metafield => {
+ if (variantIds.includes(metafield.productVariantId as string)) {
+ gatsbyApi.actions.deleteNode(metafield)
+ }
+ })
+
+ return objects.map(builder.buildNode)
+}
diff --git a/packages/gatsby-source-shopify/src/processors/index.ts b/packages/gatsby-source-shopify/src/processors/index.ts
new file mode 100644
index 0000000000000..d6d75574ae031
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/processors/index.ts
@@ -0,0 +1,2 @@
+export * from "./collections"
+export * from "./incremental-products"
diff --git a/packages/gatsby-source-shopify/src/queries.js b/packages/gatsby-source-shopify/src/queries.js
deleted file mode 100644
index 60592bbb34086..0000000000000
--- a/packages/gatsby-source-shopify/src/queries.js
+++ /dev/null
@@ -1,292 +0,0 @@
-export const ARTICLES_QUERY = `
- query GetArticles($first: Int!, $after: String) {
- articles(first: $first, after: $after) {
- pageInfo {
- hasNextPage
- }
- edges {
- cursor
- node {
- author {
- bio
- email
- firstName
- lastName
- name
- }
- blog {
- id
- }
- comments(first: 250) {
- edges {
- node {
- author {
- email
- name
- }
- content
- contentHtml
- id
- }
- }
- }
- content
- contentHtml
- excerpt
- excerptHtml
- handle
- id
- handle
- image {
- altText
- id
- src
- }
- publishedAt
- tags
- title
- url
- seo {
- title
- description
- }
- }
- }
- }
- }
-`
-
-export const BLOGS_QUERY = `
- query GetBlogs($first: Int!, $after: String) {
- blogs(first: $first, after: $after) {
- pageInfo {
- hasNextPage
- }
- edges {
- cursor
- node {
- handle
- id
- handle
- title
- url
- }
- }
- }
- }
-`
-
-export const COLLECTIONS_QUERY = `
- query GetCollections($first: Int!, $after: String) {
- collections(first: $first, after: $after) {
- pageInfo {
- hasNextPage
- }
- edges {
- cursor
- node {
- description
- descriptionHtml
- handle
- id
- image {
- altText
- id
- src
- }
- products(first: 250) {
- edges {
- node {
- id
- }
- }
- }
- title
- updatedAt
- }
- }
- }
- }
-`
-
-export const PRODUCTS_QUERY = `
- query GetProducts($first: Int!, $after: String) {
- products(first: $first, after: $after) {
- pageInfo {
- hasNextPage
- }
- edges {
- cursor
- node {
- availableForSale
- createdAt
- description
- descriptionHtml
- handle
- id
- images(first: 250) {
- edges {
- node {
- id
- altText
- originalSrc
- }
- }
- }
- metafields(first: 250) {
- edges {
- node {
- description
- id
- key
- namespace
- value
- valueType
- }
- }
- }
- onlineStoreUrl
- options {
- id
- name
- values
- }
- priceRange {
- minVariantPrice {
- amount
- currencyCode
- }
- maxVariantPrice {
- amount
- currencyCode
- }
- }
- productType
- publishedAt
- tags
- title
- updatedAt
- variants(first: 250) {
- edges {
- node {
- availableForSale
- compareAtPrice
- compareAtPriceV2 {
- amount
- currencyCode
- }
- id
- image {
- altText
- id
- originalSrc
- }
- metafields(first: 250) {
- edges {
- node {
- description
- id
- key
- namespace
- value
- valueType
- }
- }
- }
- price
- priceV2 {
- amount
- currencyCode
- }
- requiresShipping
- selectedOptions {
- name
- value
- }
- sku
- title
- weight
- weightUnit
- presentmentPrices(first: 250) {
- edges {
- node {
- price {
- amount
- currencyCode
- }
- compareAtPrice {
- amount
- currencyCode
- }
- }
- }
- }
- }
- }
- }
- vendor
- }
- }
- }
- }
-`
-
-export const SHOP_DETAILS_QUERY = `
-query GetShop {
- shop {
- description
- moneyFormat
- name
- }
-}
-`
-
-export const SHOP_POLICIES_QUERY = `
- query GetPolicies {
- shop {
- privacyPolicy {
- body
- handle
- id
- title
- url
- }
- refundPolicy {
- body
- handle
- id
- title
- url
- }
- termsOfService {
- body
- handle
- id
- title
- url
- }
- }
- }
-`
-
-export const PAGES_QUERY = `
- query GetPages($first: Int!, $after: String) {
- pages(first: $first, after: $after) {
- pageInfo {
- hasNextPage
- }
- edges {
- cursor
- node {
- id
- handle
- title
- body
- bodySummary
- updatedAt
- url
- }
- }
- }
- }
-`
diff --git a/packages/gatsby-source-shopify/src/query-builders/bulk-query.ts b/packages/gatsby-source-shopify/src/query-builders/bulk-query.ts
new file mode 100644
index 0000000000000..4ecb742bc5887
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/query-builders/bulk-query.ts
@@ -0,0 +1,30 @@
+export abstract class BulkQuery {
+ pluginOptions: ShopifyPluginOptions
+
+ constructor(pluginOptions: ShopifyPluginOptions) {
+ this.pluginOptions = pluginOptions
+ }
+
+ abstract query(date?: Date): string
+
+ protected bulkOperationQuery(query: string): string {
+ return `
+ mutation INITIATE_BULK_OPERATION {
+ bulkOperationRunQuery(
+ query: """
+ ${query}
+ """
+ ) {
+ bulkOperation {
+ id
+ status
+ }
+ userErrors {
+ field
+ message
+ }
+ }
+ }
+ `
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/query-builders/collections-query.ts b/packages/gatsby-source-shopify/src/query-builders/collections-query.ts
new file mode 100644
index 0000000000000..4059506bf4073
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/query-builders/collections-query.ts
@@ -0,0 +1,100 @@
+import { BulkQuery } from "./bulk-query"
+
+export class CollectionsQuery extends BulkQuery {
+ query(date?: Date): string {
+ const publishedStatus = this.pluginOptions.salesChannel
+ ? encodeURIComponent(`${this.pluginOptions.salesChannel}=visible`)
+ : `published`
+
+ const filters = [`published_status:${publishedStatus}`]
+ if (date) {
+ const isoDate = date.toISOString()
+ filters.push(`created_at:>='${isoDate}' OR updated_at:>='${isoDate}'`)
+ }
+
+ const queryString = filters.map(f => `(${f})`).join(` AND `)
+
+ const query = `
+ {
+ collections(query: "${queryString}") {
+ edges {
+ node {
+ products {
+ edges {
+ node {
+ id
+ }
+ }
+ }
+ metafields {
+ edges {
+ node {
+ createdAt
+ description
+ id
+ key
+ legacyResourceId
+ namespace
+ ownerType
+ updatedAt
+ value
+ valueType
+ }
+ }
+ }
+ description
+ descriptionHtml
+ feedback {
+ details {
+ app {
+ id
+ }
+ link {
+ label
+ url
+ }
+ messages {
+ field
+ message
+ }
+ }
+ summary
+ }
+ handle
+ id
+ image {
+ id
+ altText
+ height
+ width
+ originalSrc
+ transformedSrc
+ }
+ legacyResourceId
+ productsCount
+ ruleSet {
+ appliedDisjunctively
+ rules {
+ column
+ condition
+ relation
+ }
+ }
+ seo {
+ description
+ title
+ }
+ sortOrder
+ storefrontId
+ templateSuffix
+ title
+ updatedAt
+ }
+ }
+ }
+ }
+ `
+
+ return this.bulkOperationQuery(query)
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/query-builders/orders-query.ts b/packages/gatsby-source-shopify/src/query-builders/orders-query.ts
new file mode 100644
index 0000000000000..32e7b94fb50bc
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/query-builders/orders-query.ts
@@ -0,0 +1,43 @@
+import { BulkQuery } from "./bulk-query"
+
+export class OrdersQuery extends BulkQuery {
+ query(date?: Date): string {
+ const filters = []
+ if (date) {
+ const isoDate = date.toISOString()
+ filters.push(`created_at:>='${isoDate}' OR updated_at:>='${isoDate}'`)
+ }
+
+ const queryString = filters.map(f => `(${f})`).join(` AND `)
+
+ const query = `
+ {
+ orders(query: "${queryString}") {
+ edges {
+ node {
+ id
+ edited
+ closed
+ closedAt
+ refunds {
+ id
+ createdAt
+ }
+ lineItems {
+ edges {
+ node {
+ id
+ product {
+ id
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }`
+
+ return this.bulkOperationQuery(query)
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/query-builders/products-query.ts b/packages/gatsby-source-shopify/src/query-builders/products-query.ts
new file mode 100644
index 0000000000000..9acbf1845561e
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/query-builders/products-query.ts
@@ -0,0 +1,205 @@
+import { BulkQuery } from "./bulk-query"
+
+export class ProductsQuery extends BulkQuery {
+ query(date?: Date): string {
+ const publishedStatus = this.pluginOptions.salesChannel
+ ? encodeURIComponent(`${this.pluginOptions.salesChannel}=visible`)
+ : `published`
+
+ const filters = [`status:active`, `published_status:${publishedStatus}`]
+ if (date) {
+ const isoDate = date.toISOString()
+ filters.push(`created_at:>='${isoDate}' OR updated_at:>='${isoDate}'`)
+ }
+
+ const ProductVariantSortKey = `POSITION`
+ const ProductImageSortKey = `POSITION`
+
+ const queryString = filters.map(f => `(${f})`).join(` AND `)
+
+ const query = `
+ {
+ products(query: "${queryString}") {
+ edges {
+ node {
+ id
+ storefrontId
+ createdAt
+ description
+ descriptionHtml
+ featuredImage {
+ id
+ altText
+ height
+ width
+ originalSrc
+ transformedSrc
+ }
+ featuredMedia {
+ alt
+ mediaContentType
+ mediaErrors {
+ details
+ }
+ preview {
+ image {
+ id
+ altText
+ height
+ width
+ originalSrc
+ transformedSrc
+ }
+ status
+ }
+ status
+ }
+ feedback {
+ details {
+ app {
+ id
+ }
+ link {
+ label
+ url
+ }
+ messages {
+ field
+ message
+ }
+ }
+ summary
+ }
+ giftCardTemplateSuffix
+ handle
+ hasOnlyDefaultVariant
+ hasOutOfStockVariants
+ isGiftCard
+ legacyResourceId
+ mediaCount
+ onlineStorePreviewUrl
+ onlineStoreUrl
+ options {
+ id
+ name
+ position
+ values
+ }
+ priceRangeV2 {
+ maxVariantPrice {
+ amount
+ currencyCode
+ }
+ minVariantPrice {
+ amount
+ currencyCode
+ }
+ }
+ productType
+ publishedAt
+ requiresSellingPlan
+ sellingPlanGroupCount
+ seo {
+ description
+ title
+ }
+ status
+ tags
+ templateSuffix
+ title
+ totalInventory
+ totalVariants
+ tracksInventory
+ updatedAt
+ vendor
+ images(sortKey: ${ProductImageSortKey}) {
+ edges {
+ node {
+ id
+ altText
+ src
+ originalSrc
+ width
+ height
+ }
+ }
+ }
+ metafields {
+ edges {
+ node {
+ createdAt
+ description
+ id
+ key
+ legacyResourceId
+ namespace
+ ownerType
+ updatedAt
+ value
+ valueType
+ }
+ }
+ }
+ variants(sortKey: ${ProductVariantSortKey}) {
+ edges {
+ node {
+ availableForSale
+ barcode
+ compareAtPrice
+ createdAt
+ displayName
+ id
+ image {
+ id
+ altText
+ height
+ width
+ originalSrc
+ transformedSrc
+ }
+ inventoryPolicy
+ inventoryQuantity
+ legacyResourceId
+ position
+ price
+ selectedOptions {
+ name
+ value
+ }
+ sellingPlanGroupCount
+ sku
+ storefrontId
+ taxCode
+ taxable
+ title
+ updatedAt
+ weight
+ weightUnit
+ metafields {
+ edges {
+ node {
+ createdAt
+ description
+ id
+ key
+ legacyResourceId
+ namespace
+ ownerType
+ updatedAt
+ value
+ valueType
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ `
+
+ return this.bulkOperationQuery(query)
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/resolve-gatsby-image-data.ts b/packages/gatsby-source-shopify/src/resolve-gatsby-image-data.ts
new file mode 100644
index 0000000000000..188b6ccb5cd10
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/resolve-gatsby-image-data.ts
@@ -0,0 +1,121 @@
+import { fetchRemoteFile } from "gatsby-core-utils"
+import {
+ generateImageData,
+ getLowResolutionImageURL,
+ IGatsbyImageData,
+ IGatsbyImageHelperArgs,
+ IImage,
+ ImageFormat,
+} from "gatsby-plugin-image"
+import { readFileSync } from "fs"
+import { IShopifyImage, urlBuilder } from "./get-shopify-image"
+
+type ImageLayout = "constrained" | "fixed" | "fullWidth"
+
+type IImageWithPlaceholder = IImage & {
+ placeholder: string
+}
+
+async function getImageBase64({
+ imageAddress,
+ cache,
+}: {
+ imageAddress: string
+ cache: any
+}): Promise {
+ // Downloads file to the site cache and returns the file path for the given image (this is a path on the host system, not a URL)
+ const filePath = await fetchRemoteFile({
+ url: imageAddress,
+ cache,
+ })
+ const buffer = readFileSync(filePath)
+ return buffer.toString(`base64`)
+}
+
+/**
+ * Download and generate a low-resolution placeholder
+ *
+ * @param lowResImageFile
+ */
+function getBase64DataURI({ imageBase64 }: { imageBase64: string }): string {
+ return `data:image/png;base64,${imageBase64}`
+}
+
+export function makeResolveGatsbyImageData(cache: any) {
+ return async function resolveGatsbyImageData(
+ image: Node & IShopifyImage,
+ {
+ formats = [`auto`],
+ layout = `constrained`,
+ ...options
+ }: { formats: Array; layout: ImageLayout }
+ ): Promise {
+ const remainingOptions = options as Record
+ let [basename] = image.originalSrc.split(`?`)
+
+ const dot = basename.lastIndexOf(`.`)
+ let ext = ``
+ if (dot !== -1) {
+ ext = basename.slice(dot + 1)
+ basename = basename.slice(0, dot)
+ }
+
+ const generateImageSource: IGatsbyImageHelperArgs["generateImageSource"] = (
+ filename,
+ width,
+ height,
+ toFormat
+ ): IImageWithPlaceholder => {
+ return {
+ width,
+ height,
+ placeholder: ``,
+ format: toFormat,
+ src: urlBuilder({
+ width,
+ height,
+ baseUrl: filename,
+ format: toFormat,
+ options: {},
+ }),
+ }
+ }
+ const sourceMetadata = {
+ width: image.width,
+ height: image.height,
+ format: ext as ImageFormat,
+ }
+
+ if (remainingOptions && remainingOptions.placeholder === `BLURRED`) {
+ // This function returns the URL for a 20px-wide image, to use as a blurred placeholder
+ const lowResImageURL = getLowResolutionImageURL({
+ ...remainingOptions,
+ aspectRatio: remainingOptions.width / remainingOptions.height, // Workaround - fixes height being NaN; we can remove this once gatsby-plugin-image is fixed
+ formats,
+ layout,
+ sourceMetadata,
+ pluginName: `gatsby-source-shopify`,
+ filename: image.originalSrc,
+ generateImageSource,
+ })
+ const imageBase64 = await getImageBase64({
+ imageAddress: lowResImageURL,
+ cache,
+ })
+
+ // This would be your own function to download and generate a low-resolution placeholder
+ remainingOptions.placeholderURL = getBase64DataURI({
+ imageBase64,
+ })
+ }
+ return generateImageData({
+ ...remainingOptions,
+ formats,
+ layout,
+ sourceMetadata,
+ pluginName: `gatsby-source-shopify`,
+ filename: image.originalSrc,
+ generateImageSource,
+ })
+ }
+}
diff --git a/packages/gatsby-source-shopify/src/rest.ts b/packages/gatsby-source-shopify/src/rest.ts
new file mode 100644
index 0000000000000..0bf17c7f59da1
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/rest.ts
@@ -0,0 +1,48 @@
+import fetch, { Response } from "node-fetch"
+
+const getBaseUrl = (options: ShopifyPluginOptions): string =>
+ `https://${options.storeUrl}/admin/api/2021-01`
+
+export function makeShopifyFetch(
+ options: ShopifyPluginOptions
+): (path: string) => Promise {
+ const baseUrl = getBaseUrl(options)
+
+ async function shopifyFetch(
+ path: string,
+ fetchOptions = {
+ headers: {
+ "X-Shopify-Access-Token": options.password,
+ },
+ },
+ retries = 3
+ ): Promise {
+ /**
+ * This is kind of a hack, but...
+ *
+ * We do this because although callers will use a relative path,
+ * some responses might have pagination links that get fed back
+ * to shopifyFetch to retrieve the next page. Those links are
+ * absolute URLs so we account for both, but not in a very robust
+ * fashion.
+ */
+ const url = path.includes(options.storeUrl) ? path : `${baseUrl}${path}`
+
+ const resp = await fetch(url, fetchOptions)
+
+ if (!resp.ok) {
+ if (retries > 0) {
+ if (resp.status === 429) {
+ // rate limit
+ const retryAfter = parseFloat(resp.headers.get(`Retry-After`) || ``)
+ await new Promise(resolve => setTimeout(resolve, retryAfter))
+ return shopifyFetch(path, fetchOptions, retries - 1)
+ }
+ }
+ }
+
+ return resp
+ }
+
+ return async (path: string): Promise => shopifyFetch(path)
+}
diff --git a/packages/gatsby-source-shopify/src/static-queries.ts b/packages/gatsby-source-shopify/src/static-queries.ts
new file mode 100644
index 0000000000000..855ee575f3c39
--- /dev/null
+++ b/packages/gatsby-source-shopify/src/static-queries.ts
@@ -0,0 +1,49 @@
+export const OPERATION_STATUS_QUERY = `
+ query OPERATION_STATUS {
+ currentBulkOperation {
+ id
+ status
+ errorCode
+ createdAt
+ completedAt
+ objectCount
+ fileSize
+ url
+ partialDataUrl
+ query
+ }
+ }
+`
+
+export const OPERATION_BY_ID = `
+ query OPERATION_BY_ID($id: ID!) {
+ node(id: $id) {
+ ... on BulkOperation {
+ id
+ status
+ errorCode
+ createdAt
+ completedAt
+ objectCount
+ fileSize
+ url
+ partialDataUrl
+ query
+ }
+ }
+ }
+ `
+
+export const CANCEL_OPERATION = `
+ mutation CANCEL_OPERATION($id: ID!) {
+ bulkOperationCancel(id: $id) {
+ bulkOperation {
+ status
+ }
+ userErrors {
+ field
+ message
+ }
+ }
+ }
+ `
diff --git a/packages/gatsby-source-shopify/tsconfig.json b/packages/gatsby-source-shopify/tsconfig.json
new file mode 100644
index 0000000000000..cab823f261c46
--- /dev/null
+++ b/packages/gatsby-source-shopify/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "include": ["src/**/*.ts", "types"],
+ "exclude": ["node_modules"],
+ "compilerOptions": {
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true,
+ "module": "commonjs",
+ "removeComments": false,
+ "preserveConstEnums": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "sourceMap": true,
+ "target": "es2017",
+ "declaration": true,
+ "lib": ["es2017", "dom", "esnext.asynciterable"]
+ }
+}
diff --git a/packages/gatsby-source-shopify/types/interface.d.ts b/packages/gatsby-source-shopify/types/interface.d.ts
new file mode 100644
index 0000000000000..a527447da8883
--- /dev/null
+++ b/packages/gatsby-source-shopify/types/interface.d.ts
@@ -0,0 +1,64 @@
+interface ShopifyPluginOptions {
+ apiKey: string;
+ password: string;
+ storeUrl: string;
+ downloadImages?: boolean;
+ shopifyConnections?: string[];
+ typePrefix?: string;
+ salesChannel?: string;
+}
+
+interface NodeBuilder {
+ buildNode: (obj: Record) => Promise;
+}
+
+type BulkResult = Record;
+type BulkResults = BulkResult[];
+
+type BulkOperationStatus =
+ | "CANCELED"
+ | "CANCELING"
+ | "COMPLETED"
+ | "CREATED"
+ | "EXPIRED"
+ | "FAILED"
+ | "RUNNING";
+
+interface BulkOperationNode {
+ status: BulkOperationStatus;
+ /**
+ * FIXME: The docs say objectCount is a number, but it's a string. Let's
+ * follow up with Shopify on this and make sure it's working as intended.
+ */
+ objectCount: string;
+ url: string;
+ id: string;
+ errorCode?: "ACCESS_DENIED" | "INTERNAL_SERVER_ERROR" | "TIMEOUT";
+ query: string;
+}
+
+interface CurrentBulkOperationResponse {
+ currentBulkOperation: {
+ id: string;
+ status: BulkOperationStatus;
+ };
+}
+
+interface UserError {
+ field?: string[];
+ message: string;
+}
+
+interface BulkOperationRunQueryResponse {
+ bulkOperationRunQuery: {
+ userErrors: UserError[];
+ bulkOperation: BulkOperationNode;
+ };
+}
+
+interface BulkOperationCancelResponse {
+ bulkOperationCancel: {
+ bulkOperation: BulkOperationNode;
+ userErrors: UserError[];
+ };
+}
diff --git a/renovate.json5 b/renovate.json5
index b62f7091cb066..bfb77da69843e 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -18568,7 +18568,8 @@
"groupName": "minor and patch dependencies for gatsby-source-shopify",
"groupSlug": "gatsby-source-shopify-prod-minor",
"matchPackageNames": [
- "gatsby-node-helpers"
+ "sharp",
+ "shift-left"
],
"matchUpdateTypes": [
"patch"
@@ -18603,7 +18604,8 @@
"groupName": "major dependencies for gatsby-source-shopify",
"groupSlug": "gatsby-source-shopify-prod-major",
"matchPackageNames": [
- "gatsby-node-helpers"
+ "sharp",
+ "shift-left"
],
"matchUpdateTypes": [
"major",
diff --git a/yarn.lock b/yarn.lock
index a6eb87122d349..90a92392fef64 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3486,7 +3486,7 @@
call-me-maybe "^1.0.1"
glob-to-regexp "^0.3.0"
-"@mswjs/cookies@^0.1.5":
+"@mswjs/cookies@^0.1.4", "@mswjs/cookies@^0.1.5":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-0.1.6.tgz#176f77034ab6d7373ae5c94bcbac36fee8869249"
integrity sha512-A53XD5TOfwhpqAmwKdPtg1dva5wrng2gH5xMvklzbd9WLTSVU953eCRa8rtrrm6G7Cy60BOGsBRN89YQK0mlKA==
@@ -4627,6 +4627,14 @@
dependencies:
jest-diff "^24.3.0"
+"@types/jest@^26.0.20":
+ version "26.0.23"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7"
+ integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==
+ dependencies:
+ jest-diff "^26.0.0"
+ pretty-format "^26.0.0"
+
"@types/joi@^14.3.4":
version "14.3.4"
resolved "https://registry.yarnpkg.com/@types/joi/-/joi-14.3.4.tgz#eed1e14cbb07716079c814138831a520a725a1e0"
@@ -4701,7 +4709,7 @@
dependencies:
"@types/node" "*"
-"@types/node-fetch@2", "@types/node-fetch@^2.5.10":
+"@types/node-fetch@2", "@types/node-fetch@^2.5.10", "@types/node-fetch@^2.5.8":
version "2.5.10"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132"
integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ==
@@ -4724,6 +4732,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.1.tgz#5e07e0cb2ff793aa7a1b41deae76221e6166049f"
integrity sha512-/tpUyFD7meeooTRwl3sYlihx2BrJE7q9XF71EguPFIySj9B7qgnRtHsHTho+0AUm4m1SvWGm6uSncrR94q6Vtw==
+"@types/node@^14.14.34":
+ version "14.17.3"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.3.tgz#6d327abaa4be34a74e421ed6409a0ae2f47f4c3d"
+ integrity sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==
+
"@types/node@^8.5.7":
version "8.10.59"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.59.tgz#9e34261f30183f9777017a13d185dfac6b899e04"
@@ -4884,6 +4897,13 @@
dependencies:
"@types/node" "*"
+"@types/sharp@^0.28.0":
+ version "0.28.3"
+ resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.28.3.tgz#0e57ede34d3e334632ab7a68a6af070aa0f51ceb"
+ integrity sha512-y3mxUj3jukIWgdu9CrSTSCo5HruTzDxdjn5SqdIEALdTszkcot9r8HX/7q9FMx3YjuXifTD0OI+d4wA6Pogqmw==
+ dependencies:
+ "@types/node" "*"
+
"@types/signal-exit@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/signal-exit/-/signal-exit-3.0.0.tgz#75e3b17660cf1f6c6cb8557675b4e680e43bbf36"
@@ -10774,7 +10794,7 @@ duplexer@0.1.1:
version "0.1.1"
resolved "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
-duplexer@^0.1.1, duplexer@^0.1.2:
+duplexer@^0.1.1, duplexer@^0.1.2, duplexer@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
@@ -11604,6 +11624,19 @@ event-source-polyfill@^1.0.15:
resolved "https://registry.yarnpkg.com/event-source-polyfill/-/event-source-polyfill-1.0.15.tgz#a28e116281be677af4b055b67d95517e35c92435"
integrity sha512-IVmd8jWwX6ag5rXIdVCPBjBChiHBceLb1/7aKPIK7CUeJ5Br7alx029+ZpQlK4jW4Hk2qncy3ClJP97S8ltvmg==
+event-stream@=3.3.4:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
+ integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=
+ dependencies:
+ duplexer "~0.1.1"
+ from "~0"
+ map-stream "~0.1.0"
+ pause-stream "0.0.11"
+ split "0.3"
+ stream-combiner "~0.0.4"
+ through "~2.3.1"
+
event-target-shim@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
@@ -11646,6 +11679,19 @@ exec-sh@^0.3.2:
resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b"
integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==
+execa@^0.6.0:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-0.6.3.tgz#57b69a594f081759c69e5370f0d17b9cb11658fe"
+ integrity sha1-V7aaWU8IF1nGnlNw8NF7nLEWWP4=
+ dependencies:
+ cross-spawn "^5.0.1"
+ get-stream "^3.0.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
execa@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
@@ -12626,6 +12672,11 @@ from2@^2.1.0, from2@^2.1.1:
inherits "^2.0.1"
readable-stream "^2.0.0"
+from@~0:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
+ integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=
+
fs-access@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a"
@@ -12789,14 +12840,6 @@ gatsby-interface@^0.0.244:
markdown-to-jsx "^7.0.0"
theme-ui "^0.2.49"
-gatsby-node-helpers@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/gatsby-node-helpers/-/gatsby-node-helpers-0.3.0.tgz#3bdca3b7902a702a5834fef280ad66d51099d57c"
- dependencies:
- json-stringify-safe "^5.0.1"
- lodash "^4.17.4"
- p-is-promise "^1.1.0"
-
gatsby-plugin-webfonts@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/gatsby-plugin-webfonts/-/gatsby-plugin-webfonts-1.1.4.tgz#f6fb8daf0acc4c59511c98964fceca35504014ac"
@@ -14809,7 +14852,7 @@ inquirer@^6.2.0:
strip-ansi "^5.1.0"
through "^2.3.6"
-inquirer@^7.0.0:
+inquirer@^7.0.0, inquirer@^7.3.3:
version "7.3.3"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003"
integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==
@@ -18139,6 +18182,11 @@ map-obj@^4.0.0:
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5"
integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==
+map-stream@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
+ integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=
+
map-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
@@ -19190,6 +19238,30 @@ msw@^0.25.0:
strict-event-emitter "^0.1.0"
yargs "^16.2.0"
+msw@^0.27.1:
+ version "0.27.2"
+ resolved "https://registry.yarnpkg.com/msw/-/msw-0.27.2.tgz#c79530e04513e5d491a149c9181bc594bad75265"
+ integrity sha512-PjxQ06gi2mqNINzVKL/lVWiP6Dd2LDUT3QK9AS2vJMbz/Xa0FgKmd1RF7kyFKiwv6qEazVp74TS0Qc8yjXRUgA==
+ dependencies:
+ "@mswjs/cookies" "^0.1.4"
+ "@open-draft/until" "^1.0.3"
+ "@types/cookie" "^0.4.0"
+ "@types/inquirer" "^7.3.1"
+ "@types/js-levenshtein" "^1.1.0"
+ chalk "^4.1.0"
+ chokidar "^3.4.2"
+ cookie "^0.4.1"
+ graphql "^15.4.0"
+ headers-utils "^1.2.0"
+ inquirer "^7.3.3"
+ js-levenshtein "^1.1.6"
+ node-fetch "^2.6.1"
+ node-match-path "^0.6.1"
+ node-request-interceptor "^0.6.3"
+ statuses "^2.0.0"
+ strict-event-emitter "^0.1.0"
+ yargs "^16.2.0"
+
msw@^0.29.0:
version "0.29.0"
resolved "https://registry.yarnpkg.com/msw/-/msw-0.29.0.tgz#7242d575cb01db0c925241587df1fc2b79230d78"
@@ -19578,7 +19650,7 @@ node-libs-browser@^2.2.1:
util "^0.11.0"
vm-browserify "^1.0.1"
-node-match-path@^0.6.0, node-match-path@^0.6.3:
+node-match-path@^0.6.0, node-match-path@^0.6.1, node-match-path@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/node-match-path/-/node-match-path-0.6.3.tgz#55dd8443d547f066937a0752dce462ea7dc27551"
integrity sha512-fB1reOHKLRZCJMAka28hIxCwQLxGmd7WewOCBDYKpyA1KXi68A7vaGgdZAPhY2E6SXoYt3KqYCCvXLJ+O0Fu/Q==
@@ -20371,11 +20443,6 @@ p-is-promise@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e"
-p-iteration@^1.1.8:
- version "1.1.8"
- resolved "https://registry.yarnpkg.com/p-iteration/-/p-iteration-1.1.8.tgz#14df726d55af368beba81bcc92a26bb1b48e714a"
- integrity sha512-IMFBSDIYcPNnW7uWYGrBqmvTiq7W0uB0fJn6shQZs7dlF3OvrHOre+JT9ikSZ7gZS3vWqclVgoQSvToJrns7uQ==
-
p-limit@3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe"
@@ -20939,6 +21006,13 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+pause-stream@0.0.11:
+ version "0.0.11"
+ resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
+ integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=
+ dependencies:
+ through "~2.3"
+
pbkdf2@^3.0.3:
version "3.0.16"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c"
@@ -21897,6 +21971,13 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+prettier-check@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/prettier-check/-/prettier-check-2.0.0.tgz#edd086ee12d270579233ccb136a16e6afcfba1ae"
+ integrity sha512-HZG53XQTJ9Cyi5hi1VFVVFxdlhITJybpZAch3ib9KqI05VUxV+F5Hip0GhSWRItrlDzVyqjSoDQ9KqIn7AHYyw==
+ dependencies:
+ execa "^0.6.0"
+
prettier-linter-helpers@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
@@ -21913,6 +21994,11 @@ prettier@^2.0.5, prettier@^2.3.0:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18"
integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==
+prettier@^2.2.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6"
+ integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==
+
pretty-bytes@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-3.0.1.tgz#27d0008d778063a0b4811bb35c79f1bd5d5fbccf"
@@ -22165,6 +22251,13 @@ prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
+ps-tree@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd"
+ integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==
+ dependencies:
+ event-stream "=3.3.4"
+
pseudomap@^1.0.1, pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@@ -25154,6 +25247,11 @@ shellwords@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
+shift-left@^0.1.5:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/shift-left/-/shift-left-0.1.5.tgz#4a61a9d0412b1b32f9abbd97f72466c9c74a6be1"
+ integrity sha512-55d8QaP1YmuL1D52fhgq8CT1tXksM/2WPZ6980RtkMbl0Cze++kuJy50GLMnXwostk/YG9hasaJmP3r+d3yUtQ==
+
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
@@ -25588,6 +25686,13 @@ split2@^3.0.0:
dependencies:
readable-stream "^3.0.0"
+split@0.3:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
+ integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=
+ dependencies:
+ through "2"
+
split@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
@@ -25786,6 +25891,13 @@ stream-combiner2@^1.1.1:
duplexer2 "~0.1.0"
readable-stream "^2.0.2"
+stream-combiner@~0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
+ integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=
+ dependencies:
+ duplexer "~0.1.1"
+
stream-each@^1.1.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae"
@@ -25855,6 +25967,11 @@ string-argv@0.3.1:
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
+string-argv@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738"
+ integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==
+
string-env-interpolation@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152"
@@ -26777,7 +26894,7 @@ through2@^4.0.0:
dependencies:
readable-stream "3"
-through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3.4:
+through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1, through@~2.3.4:
version "2.3.8"
resolved "http://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@@ -27161,6 +27278,17 @@ ts-pnp@^1.1.6:
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a"
integrity sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ==
+tsc-watch@^4.2.9:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/tsc-watch/-/tsc-watch-4.4.0.tgz#3ebbf1db54bcef6bfe534b330fa87284a4139320"
+ integrity sha512-+0Yey6ptOOXAnt44OKTk2/EnQnmA0auL7VWXm9d9abMS4tabt0Xdr9B4AK6OJbWAre9ZdLA81+Nk8sz9unptyA==
+ dependencies:
+ cross-spawn "^7.0.3"
+ node-cleanup "^2.1.2"
+ ps-tree "^1.2.0"
+ string-argv "^0.1.1"
+ strip-ansi "^6.0.0"
+
tsconfig-paths@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
@@ -27332,6 +27460,11 @@ typescript@^4.1.3, typescript@^4.1.5:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72"
integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==
+typescript@^4.2.3:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
+ integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
+
typography-normalize@^0.16.19:
version "0.16.19"
resolved "https://registry.yarnpkg.com/typography-normalize/-/typography-normalize-0.16.19.tgz#58e0cf12466870c5b27006daa051fe7307780660"