@mayurgodhani/ecomtools-cli
Version:
E-commerce tools MCP server for Shopify development
355 lines (314 loc) • 9.93 kB
JavaScript
#!/usr/bin/env node
/**
* Setup script for ecomTools
* Runs during npm installation to ensure all required files and directories exist
*/
const fs = require('fs');
const path = require('path');
// Paths to ensure exist
const directories = [
path.join(__dirname, '../data')
];
// Ensure required directories exist
directories.forEach(dir => {
if (!fs.existsSync(dir)) {
console.log(`Creating directory: ${dir}`);
fs.mkdirSync(dir, { recursive: true });
}
});
// Create sample data files if they don't exist
const sampleDataFiles = [
{
path: path.join(__dirname, '../data/cart-drawer.txt'),
content: `Title: Shopify Cart Drawer
Description: A responsive and customizable cart drawer for Shopify themes with quantity controls, subtotals, and checkout button.
Code:
<!-- Cart Drawer HTML -->
<div id="cart-drawer" class="cart-drawer">
<div class="cart-drawer__header">
<h2>Your Cart</h2>
<button class="cart-drawer__close" aria-label="Close cart">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
<div class="cart-drawer__content">
{%- if cart.item_count > 0 -%}
<form action="{{ routes.cart_url }}" method="post" id="cart-drawer-form">
{%- for item in cart.items -%}
<div class="cart-drawer__item" data-item-key="{{ item.key }}">
<div class="cart-drawer__item-image">
<img src="{{ item.image | img_url: '120x' }}" alt="{{ item.title | escape }}" width="70" height="70">
</div>
<div class="cart-drawer__item-details">
<a href="{{ item.url }}" class="cart-drawer__item-title">
{{ item.product.title }}
</a>
<div class="cart-drawer__item-variant">
{{ item.variant.title }}
</div>
<div class="cart-drawer__item-price">
{{ item.final_price | money }}
</div>
<div class="cart-drawer__item-quantity">
<button type="button" class="quantity-button" data-action="decrease">-</button>
<input type="number" name="updates[{{ item.key }}]" value="{{ item.quantity }}" min="0" aria-label="Quantity">
<button type="button" class="quantity-button" data-action="increase">+</button>
</div>
<button type="button" class="cart-drawer__item-remove" data-item-key="{{ item.key }}">
Remove
</button>
</div>
</div>
{%- endfor -%}
<div class="cart-drawer__footer">
<div class="cart-drawer__subtotal">
<span>Subtotal</span>
<span>{{ cart.total_price | money }}</span>
</div>
<div class="cart-drawer__taxes">
<small>Tax included. <a href="{{ shop.policies.shipping_policy.url }}">Shipping</a> calculated at checkout.</small>
</div>
<button type="submit" name="checkout" class="cart-drawer__checkout-button">
Checkout
</button>
</div>
</form>
{%- else -%}
<div class="cart-drawer__empty">
<p>Your cart is currently empty.</p>
<a href="{{ routes.all_products_collection_url }}" class="cart-drawer__continue-shopping">
Continue Shopping
</a>
</div>
{%- endif -%}
</div>
</div>
<div id="cart-overlay" class="cart-overlay"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Cart drawer functionality
const cartDrawer = document.getElementById('cart-drawer');
const cartToggle = document.querySelectorAll('.cart-toggle');
const cartClose = document.querySelector('.cart-drawer__close');
const cartOverlay = document.getElementById('cart-overlay');
// Open cart drawer
cartToggle.forEach(function(toggle) {
toggle.addEventListener('click', function(e) {
e.preventDefault();
cartDrawer.classList.add('cart-drawer--open');
document.body.classList.add('cart-drawer-open');
cartOverlay.classList.add('cart-overlay--active');
});
});
// Close cart drawer
if (cartClose) {
cartClose.addEventListener('click', function() {
cartDrawer.classList.remove('cart-drawer--open');
document.body.classList.remove('cart-drawer-open');
cartOverlay.classList.remove('cart-overlay--active');
});
}
if (cartOverlay) {
cartOverlay.addEventListener('click', function() {
cartDrawer.classList.remove('cart-drawer--open');
document.body.classList.remove('cart-drawer-open');
cartOverlay.classList.remove('cart-overlay--active');
});
}
// Handle quantity buttons
const quantityButtons = document.querySelectorAll('.quantity-button');
quantityButtons.forEach(function(button) {
button.addEventListener('click', function() {
const input = this.parentNode.querySelector('input');
const currentValue = parseInt(input.value);
if (this.dataset.action === 'increase') {
input.value = currentValue + 1;
} else if (this.dataset.action === 'decrease' && currentValue > 1) {
input.value = currentValue - 1;
}
// You would typically have AJAX cart update here
});
});
});
</script>
<style>
.cart-drawer {
position: fixed;
top: 0;
right: 0;
width: 400px;
max-width: 90vw;
height: 100vh;
background-color: #fff;
box-shadow: -2px 0 10px rgba(0,0,0,0.1);
z-index: 1000;
transform: translateX(100%);
transition: transform 0.3s ease-in-out;
}
.cart-drawer--open {
transform: translateX(0);
}
/* Add your additional CSS here */
</style>
`
},
{
path: path.join(__dirname, '../data/product-card.txt'),
content: `Title: Shopify Product Card
Description: A responsive product card component for displaying products in collections with image, title, price, and add to cart button.
Code:
<div class="product-card">
<a href="{{ product.url | within: collection }}" class="product-card__link">
<div class="product-card__image-wrapper">
{% if product.featured_image %}
<img
class="product-card__image"
src="{{ product.featured_image | img_url: '400x' }}"
alt="{{ product.featured_image.alt | escape }}"
width="400"
height="400"
loading="lazy"
>
{% else %}
{{ 'product-1' | placeholder_svg_tag: 'product-card__image placeholder-svg' }}
{% endif %}
{% if product.compare_at_price > product.price %}
<span class="product-card__badge">Sale</span>
{% endif %}
</div>
<div class="product-card__info">
<h3 class="product-card__title">{{ product.title }}</h3>
<div class="product-card__price">
{% if product.compare_at_price > product.price %}
<span class="product-card__price-regular">{{ product.price | money }}</span>
<span class="product-card__price-compare">{{ product.compare_at_price | money }}</span>
{% else %}
<span class="product-card__price-regular">{{ product.price | money }}</span>
{% endif %}
</div>
</div>
</a>
<div class="product-card__actions">
{% if product.available %}
<button
class="product-card__add-to-cart"
data-product-id="{{ product.id }}"
data-variant-id="{{ product.selected_or_first_available_variant.id }}"
>
Add to cart
</button>
{% else %}
<button class="product-card__sold-out" disabled>Sold out</button>
{% endif %}
</div>
</div>
<style>
.product-card {
position: relative;
margin-bottom: 30px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.product-card__link {
display: block;
text-decoration: none;
color: inherit;
}
.product-card__image-wrapper {
position: relative;
padding-top: 100%; /* 1:1 Aspect Ratio */
overflow: hidden;
}
.product-card__image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s;
}
.product-card:hover .product-card__image {
transform: scale(1.05);
}
.product-card__badge {
position: absolute;
top: 10px;
right: 10px;
background-color: #e53e3e;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: bold;
}
.product-card__info {
padding: 15px;
background-color: white;
}
.product-card__title {
margin: 0 0 10px;
font-size: 1rem;
font-weight: 500;
}
.product-card__price {
display: flex;
align-items: center;
flex-wrap: wrap;
font-weight: 500;
}
.product-card__price-regular {
font-size: 1rem;
}
.product-card__price-compare {
margin-left: 8px;
font-size: 0.875rem;
color: #666;
text-decoration: line-through;
}
.product-card__actions {
padding: 0 15px 15px;
background-color: white;
}
.product-card__add-to-cart,
.product-card__sold-out {
width: 100%;
padding: 10px 15px;
border: none;
border-radius: 4px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.product-card__add-to-cart {
background-color: #3182ce;
color: white;
}
.product-card__add-to-cart:hover {
background-color: #2c5282;
}
.product-card__sold-out {
background-color: #e2e8f0;
color: #4a5568;
cursor: not-allowed;
}
</style>
`
}
];
sampleDataFiles.forEach(file => {
if (!fs.existsSync(file.path)) {
console.log(`Creating sample data file: ${file.path}`);
fs.writeFileSync(file.path, file.content);
}
});
console.log('Setup complete!');