Skip to content
This repository was archived by the owner on Jun 18, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .eleventy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
// Plugin Imports
const pluginDirectoryOutput = require("@11ty/eleventy-plugin-directory-output");
const pluginEleventyNavigation = require("@11ty/eleventy-navigation");
const pluginShopify = require("eleventy-plugin-shopify");

// Config Imports
const configShopify = require("./src/config/plugins/shopify");

// Filter Imports
const filterGetProductsInCollection = require("./src/config/filters/getProductsInCollection");

// Filter Imports
const filterFormatDate = require("./src/config/filters/formatDate");

module.exports = function (eleventyConfig) {
/**
Expand All @@ -17,6 +27,10 @@ module.exports = function (eleventyConfig) {
// https://www.11ty.dev/docs/plugins/navigation/
eleventyConfig.addPlugin(pluginEleventyNavigation);

// Queries your Shopify store at build time to expose product and collection data under the `shopify` global object
// https://github.com/dleatherman/eleventy-plugin-shopify
eleventyConfig.addPlugin(pluginShopify, configShopify);

/**
* PASSTHROUGH'S
* Copy/paste non-template files straight to /public, without any interference from the eleventy engine
Expand All @@ -28,6 +42,18 @@ module.exports = function (eleventyConfig) {
eleventyConfig.addPassthroughCopy("./src/assets/images");
eleventyConfig.addPassthroughCopy("./src/assets/js");
eleventyConfig.addPassthroughCopy("./src/assets/svgs");
eleventyConfig.addPassthroughCopy("./src/admin");

/**
* FILTERS
* Allows modification of data before it is outputted, denoted by {{ contentToPrint | filterName }}
* https://www.11ty.dev/docs/filters/
*/

// Turns a date from ISO format to a more human-readable one
eleventyConfig.addFilter("formatDate", filterFormatDate);

eleventyConfig.addFilter("getProductsInCollection", filterGetProductsInCollection);

return {
dir: {
Expand Down
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SHOPIFY_STORE_URL=codestitch.myshopify.com
SHOPIFY_ACCESS_TOKEN=3e5d1f3b999bf5d82849f89d66ea2992
SHOPIFY_API_VERSION=2023-10
2,590 changes: 2,586 additions & 4 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "CodeStitch's Advanced Starter Kit. A kit designed for agency's and freelancers who are knowledgable in HTML, CSS, JS and SSGs, who are wanting to delivery any feature a small to medium business would need",
"main": "index.js",
"scripts": {
"watch:cms": "npx decap-server",
"build:eleventy": "cross-env ELEVENTY_ENV=PROD eleventy",
"watch:eleventy": "cross-env ELEVENTY_ENV=DEV eleventy --serve",
"start": "run-p watch:*",
Expand All @@ -23,8 +24,12 @@
"@11ty/eleventy": "^2.0.1",
"@11ty/eleventy-navigation": "^0.3.5",
"@11ty/eleventy-plugin-directory-output": "^1.0.1",
"@shopify/buy-button-js": "^2.4.0",
"cross-env": "^7.0.3",
"decap-server": "^3.0.1",
"netlify-plugin-cache": "^1.0.3",
"dotenv": "^16.3.1",
"eleventy-plugin-shopify": "^0.1.0",
"npm-run-all": "^4.1.5"
}
}
132 changes: 132 additions & 0 deletions src/_data/eleventyComputed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Gets the number from a Shopify ID
// gid://shopify/Product/8843402314018 => 8843402314018
function getIdNumber(id) {
const idLastSlash = id.lastIndexOf("/");
return id.slice(idLastSlash + 1);
}

module.exports = {
shopProducts: (data) => {
// Fixes a product price to two decimal places. Uses the Store's preferred money format to append any necessary currency symbols
// "11.0" => "$11.00"
function formatPrice(price) {
if (price === "0.0") {
return null;
}

const fixedPrice = Number(price).toFixed(2);

return data.shopify.shop.moneyFormat.replace(
"{{amount}}",
fixedPrice
);
}

// Checks product variants to see if there are any intra-product differences in pricing. If there is, prefix price with "From".
// minPrice: 9.0 and maxPrice: 19.99 => simplePrice: "From $9.00"
function getSimplePrice(priceRange) {
const formattedMinPrice = formatPrice(
priceRange.minVariantPrice.amount
);

if (!formattedMinPrice) {
return null;
} else if (
priceRange.minVariantPrice.amount ===
priceRange.maxVariantPrice.amount
) {
return formattedMinPrice;
} else {
return `From ${formattedMinPrice}`;
}
}

return data.shopify.products.map((product) => {
// Removes the edges/node from the GQL structure
const newImages = [];
product.images.edges.forEach((image) => newImages.push(image.node));

// Generates a formatted price for the product and it's min/max variant price values
const price = getSimplePrice(product.priceRange);
product.priceRange.minVariantPrice.price = formatPrice(
product.priceRange.minVariantPrice.amount
);
product.priceRange.maxVariantPrice.price = formatPrice(
product.priceRange.maxVariantPrice.amount
);

// Generates a formatted compareAt price for the product and it's min/max variant compareAt price values
const compareAtPrice = getSimplePrice(product.compareAtPriceRange);
product.compareAtPriceRange.minVariantPrice.price = formatPrice(
product.compareAtPriceRange.minVariantPrice.amount
);
product.compareAtPriceRange.maxVariantPrice.price = formatPrice(
product.compareAtPriceRange.maxVariantPrice.amount
);

// Takes the collection out of the edge/node GQL structure and adds an "idNumber to the entry"
const newCollections = product.collections.edges.map(
(collection) => {
collection.node.idNumber = getIdNumber(collection.node.id);
return collection.node;
}
);

// Works out the percentage off price for use in sale tags. Only uses the minimum prices, if multiple variant prices are defined.
const wasPrice = Number(
product.compareAtPriceRange.minVariantPrice.amount
);
const discountedPrice = Number(
product.priceRange.minVariantPrice.amount
);
let saleAmount = null;
if (wasPrice !== discountedPrice && wasPrice !== 0) {
saleAmount = (
((wasPrice - discountedPrice) / wasPrice) *
100
).toFixed(0);
}

return {
title: product.title,
id: product.id,
idNumber: getIdNumber(product.id),
handle: product.handle,
description: product.description,
descriptionHtml: product.descriptionHtml,
collections: newCollections,
images: newImages,
tags: product.tags,
compareAtPriceRange: product.compareAtPriceRange,
compareAtPrice,
priceRange: product.priceRange,
price,
saleAmount,
};
});
},

shopCollections: (data) => {
return data.shopify.collections.map((collection) => {
const collectionProducts = collection.products?.edges.map(
(product) => {
return {
id: getIdNumber(product.node.id),
idLink: product.node.id,
};
}
);

return {
id: collection.id,
idNumber: getIdNumber(collection.id),
title: collection.title,
handle: collection.handle,
description: collection.description,
descriptionHtml: collection.descriptionHtml,
image: collection.image,
products: collectionProducts,
};
});
},
};
Loading