@dscodotco/theme-cli
Version:
A CLI tool for developing Shopify themes
128 lines (126 loc) • 4.87 kB
JavaScript
/**
* @module commands/shopify/theme/init
* @description Commands for initializing Shopify themes
*/
import path from "path";
import fs from "fs";
import ora from "ora";
import { downloadFile } from "../../../utils/download.js";
import { extractZip } from "../../../utils/zip.js";
import { createLogger } from "../../../utils/logger.js";
const logger = createLogger("theme-init");
// Add debug mode flag
let isDebugMode = false;
/**
* URL to the Dawn theme zip file on GitHub's main branch
* @constant
*/
const DAWN_THEME_URL = "https://github.com/Shopify/dawn/archive/refs/heads/main.zip";
const ENV_EXAMPLE_CONTENT = `# Shopify store credentials
SHOPIFY_STORE=your-store-name # e.g. your-store.myshopify.com
SHOPIFY_API_KEY=your-api-key
SHOPIFY_PASSWORD=your-admin-api-access-token
# Development settings
PORT=3000 # Optional, defaults to 3000
DEBUG=true # Optional, enables verbose logging`;
/**
* Creates necessary configuration files in the theme directory
*/
function createConfigFiles(themeDir) {
// Create .env.example
fs.writeFileSync(path.join(themeDir, ".env.example"), ENV_EXAMPLE_CONTENT);
// Create .env if it doesn't exist
const envPath = path.join(themeDir, ".env");
if (!fs.existsSync(envPath)) {
fs.writeFileSync(envPath, ENV_EXAMPLE_CONTENT);
}
// Add .env to .gitignore if it exists
const gitignorePath = path.join(themeDir, ".gitignore");
if (fs.existsSync(gitignorePath)) {
const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
if (!gitignoreContent.includes(".env")) {
fs.appendFileSync(gitignorePath, "\n.env\n");
}
}
else {
fs.writeFileSync(gitignorePath, ".env\n");
}
}
/**
* Initializes a new Shopify theme by downloading and extracting the Dawn theme
*
* @param options - Configuration options for initializing the theme
* @returns A promise that resolves when the theme is initialized
*
* @example
* ```typescript
* // Initialize a theme with default options
* await initShopifyTheme();
*
* // Initialize a theme with custom options
* await initShopifyTheme({
* name: 'my-store-theme',
* outputDir: './projects',
* force: true
* });
* ```
*
* @throws Will throw an error if the theme initialization fails
*/
export async function initShopifyTheme(options) {
isDebugMode = options.debug || false;
if (isDebugMode) {
logger.info("Debug mode enabled");
logger.info(`Initializing theme with options: ${JSON.stringify(options, null, 2)}`);
}
const themeDir = path.resolve(options.name || ".");
if (isDebugMode) {
logger.info(`Theme directory: ${themeDir}`);
}
// Check if directory exists and is not empty
if (fs.existsSync(themeDir) && fs.readdirSync(themeDir).length > 0) {
if (!options.force) {
throw new Error(`Directory ${themeDir} already exists and is not empty. Use --force to overwrite.`);
}
if (isDebugMode) {
logger.info("Force flag enabled, proceeding with existing directory");
}
}
const spinner = ora("Downloading Shopify Dawn theme...").start();
try {
if (isDebugMode) {
logger.info(`Downloading theme from: ${DAWN_THEME_URL}`);
}
// Download the theme
const zipData = await downloadFile(DAWN_THEME_URL);
spinner.text = "Extracting theme files...";
// Extract the theme (stripping the first directory which is 'dawn-main')
if (isDebugMode) {
logger.info(`Extracting theme to: ${themeDir}`);
}
extractZip(Buffer.from(zipData), themeDir, true);
// Create configuration files
if (isDebugMode) {
logger.info("Creating configuration files...");
}
createConfigFiles(themeDir);
spinner.succeed(`Shopify theme initialized successfully in ${themeDir}`);
// Show next steps
logger.info("\nNext steps:");
logger.info(`1. cd ${options.name || "my-theme"}`);
logger.info(`2. Configure your .env file with your Shopify credentials`);
logger.info("3. Run 'npx @dscodotco/theme-cli shopify theme dev' to start the development server");
logger.info("\nFor more information, visit https://shopify.dev/themes");
// Show environment setup warning
logger.warn("\nIMPORTANT: Before running the development server, you must configure your Shopify credentials in the .env file.");
logger.info("We've created a .env file with example values. Please update it with your actual credentials.");
}
catch (error) {
spinner.fail(`Failed to initialize theme: ${error.message}`);
if (isDebugMode) {
logger.error("Stack trace:");
logger.error(error.stack || "No stack trace available");
}
throw error;
}
}