-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdev-operations.ts
More file actions
175 lines (142 loc) · 6.3 KB
/
Copy pathdev-operations.ts
File metadata and controls
175 lines (142 loc) · 6.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import { spawn, execSync } from "child_process";
import { select, isCancel, note, spinner } from "@clack/prompts";
import type { Dependencies, Result, DevOperations, CLIContext } from "./types.js";
/**
* Development server operations
*/
export const createDevOperations = (deps: Dependencies): DevOperations => ({
startDev: (shopId: string, environment: 'production' | 'staging') => startDevServer(deps, shopId, environment)
});
export const startDevelopmentWorkflow = async (context: CLIContext): Promise<Result<void>> => {
const shopsResult = await context.shopOps.listShops();
if (!shopsResult.success || !shopsResult.data?.length) {
note("No shops configured. Create a shop first.", "⚠️ Setup Required");
return { success: false, error: "No shops available" };
}
const shops = shopsResult.data;
const selectedShop = await selectShopForDevelopment(shops);
if (!selectedShop) return { success: false, error: "No shop selected" };
const environment = await selectEnvironment();
if (!environment) return { success: false, error: "No environment selected" };
const themeEditorSync = await selectThemeEditorSync();
if (themeEditorSync === null) return { success: false, error: "Selection cancelled" };
return startShopifyDevelopmentServer(context, selectedShop, environment, themeEditorSync);
};
const selectShopForDevelopment = async (shops: string[]): Promise<string | null> => {
const shopChoice = await select({
message: "Select shop for development:",
options: shops.map(shop => ({
value: shop,
label: shop,
hint: `Start dev server for ${shop}`
}))
});
return isCancel(shopChoice) ? null : shopChoice as string;
};
const selectEnvironment = async (): Promise<'staging' | 'production' | null> => {
const envChoice = await select({
message: "Select environment:",
options: [
{ value: "staging", label: "Staging", hint: "Safe for development" },
{ value: "production", label: "Production", hint: "Live store - be careful!" }
]
});
return isCancel(envChoice) ? null : envChoice as 'staging' | 'production';
};
const selectThemeEditorSync = async (): Promise<boolean | null> => {
const syncChoice = await select({
message: "Sync theme editor files to local?",
options: [
{ value: "yes", label: "Yes, sync theme editor files", hint: "Pull changes made in theme editor" },
{ value: "no", label: "No, do not sync", hint: "Ignore theme editor changes" }
]
});
if (isCancel(syncChoice)) return null;
return syncChoice === "yes";
};
const startShopifyDevelopmentServer = async (context: CLIContext, shopId: string, environment: 'production' | 'staging', themeEditorSync: boolean): Promise<Result<void>> => {
// Load shop configuration
const configResult = await context.shopOps.loadConfig(shopId);
if (!configResult.success) {
return { success: false, error: configResult.error || "Failed to load shop config" };
}
// Load credentials
const credentialsResult = await context.credOps.loadCredentials(shopId);
if (!credentialsResult.success) {
return { success: false, error: "Failed to load credentials" };
}
const credentials = credentialsResult.data;
if (!credentials) {
note(`No credentials found for ${shopId}. Set up credentials first.`, "⚠️ Setup Required");
return { success: false, error: "No credentials available" };
}
const config = configResult.data;
if (!config) {
return { success: false, error: "Config data is missing" };
}
const store = config.shopify.stores[environment];
const token = credentials.shopify.stores[environment].themeToken;
if (!token) {
note(`No theme token found for ${environment}`, "⚠️ Setup Required");
return { success: false, error: "No theme token available" };
}
return executeShopifyCLI(store.domain, token, shopId, environment, themeEditorSync);
};
const executeShopifyCLI = async (storeDomain: string, themeToken: string, shopId: string, environment: 'staging' | 'production', themeEditorSync: boolean): Promise<Result<void>> => {
const s = spinner();
s.start("Starting Shopify CLI...");
try {
// Check Shopify CLI availability
execSync('shopify version', { stdio: 'ignore' });
s.stop("✅ Starting development server");
const storeArg = `--store=${storeDomain.replace('.myshopify.com', '')}`;
const args = ['theme', 'dev', storeArg];
if (themeEditorSync) {
args.push('--theme-editor-sync');
}
console.log(`\n🔗 Development Server:`);
console.log(` Shop: ${shopId} (${environment})`);
console.log(` Store: ${storeDomain}`);
console.log(` Token: ${themeToken.substring(0, 8)}...`);
console.log(` Theme Editor Sync: ${themeEditorSync ? 'Enabled' : 'Disabled'}`);
console.log(`\n⚡ Running: shopify ${args.join(' ')}`);
console.log(`\nPress Ctrl+C to stop\n`);
// Start Shopify CLI with proper signal handling
const devProcess = spawn('shopify', args, {
env: {
...process.env,
SHOPIFY_CLI_THEME_TOKEN: themeToken,
SHOPIFY_STORE: storeDomain.replace('.myshopify.com', '')
},
stdio: 'inherit',
detached: false
});
return new Promise<Result<void>>((resolve) => {
const handleSignal = (signal: NodeJS.Signals): void => {
console.log(`\nReceived ${signal}, stopping development server...`);
devProcess.kill(signal);
};
process.on('SIGINT', handleSignal);
process.on('SIGTERM', handleSignal);
devProcess.on('close', () => {
process.off('SIGINT', handleSignal);
process.off('SIGTERM', handleSignal);
note("Development server stopped", "ℹ️ Info");
resolve({ success: true });
});
devProcess.on('error', (error) => {
process.off('SIGINT', handleSignal);
process.off('SIGTERM', handleSignal);
resolve({ success: false, error: error.message });
});
});
} catch {
s.stop("❌ Shopify CLI not found");
note("Install: pnpm add -g @shopify/cli", "Installation Required");
return { success: false, error: "Shopify CLI not available" };
}
};
const startDevServer = async (_deps: Dependencies, _shopId: string, _environment: 'production' | 'staging'): Promise<Result<void>> => {
// This is called through the DevOperations interface but we use the workflow function instead
return { success: true };
};