@dscodotco/theme-cli
Version:
A CLI tool for developing Shopify themes
146 lines (129 loc) • 3.74 kB
text/typescript
import axios from "axios";
import { createLogger } from "../logger.js";
import { createHmac } from "crypto";
const logger = createLogger("theme-manager");
export interface ShopifyCredentials {
storeName: string;
apiKey: string;
password: string;
}
export interface ShopifyTheme {
id: number;
name: string;
role: string;
theme_store_id?: number;
previewable: boolean;
processing: boolean;
admin_graphql_api_id: string;
created_at: string;
updated_at: string;
}
/**
* Manages Shopify themes through the Admin API
*/
export class ThemeManager {
private credentials: ShopifyCredentials;
private baseUrl: string;
private apiUrl: string;
private isAccessToken: boolean;
/**
* Creates a new ThemeManager instance
* @param credentials Shopify store credentials
*/
constructor(credentials: ShopifyCredentials) {
this.credentials = credentials;
this.baseUrl = `https://${credentials.storeName}.myshopify.com`;
this.apiUrl = `${this.baseUrl}/admin/api/2023-04`;
// Determine if we're using an access token (shpat_) or API key + secret
this.isAccessToken = this.credentials.password.startsWith("shpat_");
if (!this.isAccessToken) {
logger.info("Using API key + secret authentication");
} else {
logger.info("Using access token authentication");
}
}
/**
* Gets the appropriate authentication configuration for Shopify API calls
* @returns Authentication config object
*/
public getAuthConfig = (): any => {
if (this.isAccessToken) {
// Access token auth - use headers
return {
headers: {
"X-Shopify-Access-Token": this.credentials.password,
"Content-Type": "application/json",
},
};
} else {
// API key + secret auth - use basic auth
return {
auth: {
username: this.credentials.apiKey,
password: this.credentials.password,
},
headers: {
"Content-Type": "application/json",
},
};
}
};
/**
* Lists all themes in the store
* @returns Promise containing array of themes
*/
async listThemes(): Promise<ShopifyTheme[]> {
try {
const authConfig = this.getAuthConfig();
const response = await axios.get(
`${this.apiUrl}/themes.json`,
authConfig
);
return response.data.themes as ShopifyTheme[];
} catch (error) {
logger.error(`Failed to list themes: ${(error as Error).message}`);
throw error;
}
}
/**
* Creates a new development theme
* @param name The name for the new theme
* @returns The created theme
*/
async createDevelopmentTheme(name?: string): Promise<ShopifyTheme> {
const themeName = name || `Development Theme (${new Date().toISOString()})`;
const requestBody = {
theme: {
name: themeName,
role: "development",
},
};
try {
logger.info(`Creating new development theme: ${themeName}`);
const authConfig = this.getAuthConfig();
const response = await axios.post(
`${this.apiUrl}/themes.json`,
requestBody,
authConfig
);
const newTheme = response.data.theme as ShopifyTheme;
logger.success(
`Created new development theme: ${newTheme.name} (ID: ${newTheme.id})`
);
return newTheme;
} catch (error) {
logger.error(
`Failed to create development theme: ${(error as Error).message}`
);
throw error;
}
}
/**
* Gets the preview URL for a theme
* @param themeId The theme ID
* @returns The preview URL
*/
getThemePreviewUrl(themeId: number): string {
return `${this.baseUrl}/?preview_theme_id=${themeId}`;
}
}