UNPKG

@dscodotco/theme-cli

Version:

A CLI tool for developing Shopify themes

155 lines (154 loc) 6.14 kB
import path from "path"; import ora from "ora"; import { Command } from "commander"; import { SimpleDevServer } from "../../../utils/shopify/simple-dev-server.js"; import { ThemeManager } from "../../../utils/shopify/theme-manager.js"; import { createLogger } from "../../../utils/logger.js"; import Shopify from "shopify-api-node"; import dotenv from "dotenv"; const logger = createLogger("theme-dev"); /** * Load environment variables from .env file */ function loadEnvVariables() { const result = dotenv.config(); if (result.error) { logger.warn("No .env file found. You'll need to provide credentials via command line options."); return; } if (result.parsed) { logger.info("Loaded environment variables from .env file"); } } /** * Validate required credentials */ function validateCredentials(options) { const missingCredentials = []; if (!options.store) { if (process.env.SHOPIFY_STORE) { options.store = process.env.SHOPIFY_STORE; } else { missingCredentials.push("store (-s, --store)"); } } if (!options.apiKey) { if (process.env.SHOPIFY_API_KEY) { options.apiKey = process.env.SHOPIFY_API_KEY; } else { missingCredentials.push("API key (-k, --api-key)"); } } if (!options.password) { if (process.env.SHOPIFY_PASSWORD) { options.password = process.env.SHOPIFY_PASSWORD; } else { missingCredentials.push("password (-p, --password)"); } } if (missingCredentials.length > 0) { logger.error("Missing required credentials:"); missingCredentials.forEach((cred) => logger.error(` - ${cred}`)); logger.info("\nYou can provide these credentials in two ways:"); logger.info("1. Create a .env file in your theme directory with the following variables:"); logger.info(" SHOPIFY_STORE=your-store"); logger.info(" SHOPIFY_API_KEY=your-api-key"); logger.info(" SHOPIFY_PASSWORD=your-password"); logger.info("\n2. Pass them as command line options:"); logger.info(" npx @dscodotco/theme-cli shopify theme dev -s your-store -k your-api-key -p your-password"); process.exit(1); } } export const dev = new Command() .name("dev") .description("Start a local development server for theme development") .option("-s, --store <store>", "Shopify store name (e.g., my-store)") .option("-k, --api-key <key>", "Shopify Admin API key") .option("-p, --password <password>", "Shopify Admin API password/token") .option("-t, --theme-id <id>", "Theme ID to use for development") .option("-d, --theme-dir <dir>", "Theme directory (defaults to current directory)") .option("--port <port>", "Port for preview server", "3000") .option("--theme-name <n>", "Name for the development theme") .option("--debug", "Enable debug mode with verbose logging", false) .action(async (options) => { // Load environment variables loadEnvVariables(); // Validate credentials validateCredentials(options); const spinner = ora("Starting development server...").start(); try { if (options.debug) { logger.info("Debug mode enabled"); logger.info("Configuration:"); logger.info(` Store: ${options.store}`); logger.info(` Theme directory: ${options.themeDir || "."}`); logger.info(` Port: ${options.port || "3000"}`); logger.info(` Theme ID: ${options.themeId || "Creating new theme"}`); } // Initialize Shopify client const shopify = new Shopify({ shopName: options.store, accessToken: options.password, apiVersion: "2023-04", }); // Initialize theme manager const themeManager = new ThemeManager({ storeName: options.store, apiKey: options.apiKey, password: options.password, }); // Resolve theme directory const themeDir = path.resolve(options.themeDir || "."); // Get or create theme ID let themeId = options.themeId ? parseInt(options.themeId) : undefined; if (!themeId) { spinner.text = "Creating a new development theme..."; const theme = await themeManager.createDevelopmentTheme(options.themeName || "Development Theme"); themeId = theme.id; spinner.succeed(`Created new development theme: ${theme.name} (ID: ${themeId})`); } // Start development server spinner.start("Starting development server..."); const port = parseInt(options.port || "3000"); const server = new SimpleDevServer({ port, themeDir, shopify, themeManager, credentials: { storeName: options.store, apiKey: options.apiKey, password: options.password, }, themeId, debug: options.debug, }); await server.start(); spinner.succeed("Development server running"); // Log preview URL const previewUrl = themeManager.getThemePreviewUrl(themeId); logger.info(`\nShopify theme preview: ${previewUrl}`); if (!options.themeId) { logger.info("\nA new development theme was created for this session."); logger.info("It will remain in your Shopify admin until you delete it.\n"); } logger.info("Press Ctrl+C to stop the development server"); // Handle shutdown process.on("SIGINT", () => { logger.info("\nShutting down..."); server.stop(); process.exit(0); }); } catch (error) { spinner.fail(`Failed to start development server: ${error.message}`); if (options.debug && error instanceof Error) { logger.error("Stack trace:"); logger.error(error.stack || "No stack trace available"); } process.exit(1); } });