@emailshepherd/cli 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,704 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import "dotenv/config";
5
+ import { Command } from "commander";
6
+
7
+ // package.json
8
+ var version = "0.1.22";
9
+
10
+ // src/commands/init.ts
11
+ import { AxiosError } from "axios";
12
+ import { existsSync, mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2, readFileSync, readdirSync, statSync } from "fs";
13
+ import { resolve as resolve2, dirname, join } from "path";
14
+ import { fileURLToPath } from "url";
15
+ import { execSync } from "child_process";
16
+ import { input, password, select } from "@inquirer/prompts";
17
+
18
+ // src/api/axiosConfig.ts
19
+ import axios from "axios";
20
+ function configureAxios(config) {
21
+ const baseUrl = config?.baseUrl ?? process.env.EMAILSHEPHERD_BASE_URL ?? "https://api.emailshepherd.com";
22
+ const apiKey = config?.apiKey ?? process.env.EMAILSHEPHERD_API_KEY;
23
+ axios.defaults.baseURL = baseUrl;
24
+ axios.defaults.headers.common["Authorization"] = `Bearer ${apiKey}`;
25
+ axios.defaults.headers.common["Content-Type"] = "application/json";
26
+ }
27
+
28
+ // src/api/generated/apiV1.ts
29
+ import axios2 from "axios";
30
+ var getEmailDesignSystems = (workspaceId, params, options) => {
31
+ return axios2.get(
32
+ `/api/v1/workspaces/${workspaceId}/email_design_systems`,
33
+ {
34
+ ...options,
35
+ params: { ...params, ...options?.params }
36
+ }
37
+ );
38
+ };
39
+ var getEmailDesignSystem = (workspaceId, id, options) => {
40
+ return axios2.get(
41
+ `/api/v1/workspaces/${workspaceId}/email_design_systems/${id}`,
42
+ options
43
+ );
44
+ };
45
+ var updateEmailDesignSystem = (workspaceId, id, updateEmailDesignSystemBody, options) => {
46
+ return axios2.patch(
47
+ `/api/v1/workspaces/${workspaceId}/email_design_systems/${id}`,
48
+ updateEmailDesignSystemBody,
49
+ options
50
+ );
51
+ };
52
+ var renderEmailDesignSystem = (workspaceId, emailDesignSystemId, renderEmailDesignSystemBody, options) => {
53
+ return axios2.post(
54
+ `/api/v1/workspaces/${workspaceId}/email_design_systems/${emailDesignSystemId}/renders`,
55
+ renderEmailDesignSystemBody,
56
+ options
57
+ );
58
+ };
59
+ var getComponents = (workspaceId, emailDesignSystemId, params, options) => {
60
+ return axios2.get(
61
+ `/api/v1/workspaces/${workspaceId}/email_design_systems/${emailDesignSystemId}/components`,
62
+ {
63
+ ...options,
64
+ params: { ...params, ...options?.params }
65
+ }
66
+ );
67
+ };
68
+ var createComponent = (workspaceId, emailDesignSystemId, createComponentBody, options) => {
69
+ return axios2.post(
70
+ `/api/v1/workspaces/${workspaceId}/email_design_systems/${emailDesignSystemId}/components`,
71
+ createComponentBody,
72
+ options
73
+ );
74
+ };
75
+ var updateComponent = (workspaceId, emailDesignSystemId, id, updateComponentBody, options) => {
76
+ return axios2.patch(
77
+ `/api/v1/workspaces/${workspaceId}/email_design_systems/${emailDesignSystemId}/components/${id}`,
78
+ updateComponentBody,
79
+ options
80
+ );
81
+ };
82
+ var getWorkspaces = (params, options) => {
83
+ return axios2.get(
84
+ `/api/v1/workspaces`,
85
+ {
86
+ ...options,
87
+ params: { ...params, ...options?.params }
88
+ }
89
+ );
90
+ };
91
+
92
+ // src/utils/componentFiles.ts
93
+ import { mkdirSync, writeFileSync } from "fs";
94
+ import { resolve } from "path";
95
+ var EXCLUDED_FIELDS = [
96
+ "email_design_system_id",
97
+ "last_updated_by",
98
+ "updated_at",
99
+ "created_at",
100
+ "screenshot_url",
101
+ "template",
102
+ "container",
103
+ "position"
104
+ ];
105
+ function cleanComponentForConfig(component) {
106
+ const cleaned = {};
107
+ for (const [key, value] of Object.entries(component)) {
108
+ if (!EXCLUDED_FIELDS.includes(key)) {
109
+ cleaned[key] = value;
110
+ }
111
+ }
112
+ return cleaned;
113
+ }
114
+ function writeComponentFiles(component, directory) {
115
+ writeFileSync(resolve(directory, "template.liquid"), component.template);
116
+ const cleanedComponent = cleanComponentForConfig(component);
117
+ const componentWithTemplate = { ...cleanedComponent, template: "TEMPLATE_PLACEHOLDER" };
118
+ const jsonString = JSON.stringify(componentWithTemplate, null, 2).replace('"TEMPLATE_PLACEHOLDER"', "template");
119
+ const componentContent = `import { defineComponent } from '@emailshepherd/cli/types';
120
+ import template from './template.liquid?raw';
121
+
122
+ export default defineComponent(${jsonString});
123
+ `;
124
+ writeFileSync(resolve(directory, "index.ts"), componentContent);
125
+ }
126
+ function buildComponentDirectory(component, targetDir) {
127
+ const componentDirectory = resolve(targetDir, "src", "components", component.name);
128
+ mkdirSync(componentDirectory, { recursive: true });
129
+ writeComponentFiles(component, componentDirectory);
130
+ }
131
+
132
+ // src/commands/init.ts
133
+ var __filename = fileURLToPath(import.meta.url);
134
+ var __dirname = dirname(__filename);
135
+ var TEMPLATES_DIR = join(__dirname, "..", "templates");
136
+ var BASE_URL = "https://api.emailshepherd.com";
137
+ function buildContainerComponentDirectory(component, targetDir) {
138
+ const containerDir = resolve2(targetDir, "src", "container_component");
139
+ writeComponentFiles(component, containerDir);
140
+ }
141
+ function copyTemplates(targetDir, replacements) {
142
+ const templatesDir = TEMPLATES_DIR;
143
+ const files = readdirSync(templatesDir);
144
+ for (const file of files) {
145
+ const sourcePath = join(templatesDir, file);
146
+ const isDir = statSync(sourcePath).isDirectory();
147
+ if (isDir && file === "claude") {
148
+ const destDir = join(targetDir, ".claude");
149
+ mkdirSync2(destDir, { recursive: true });
150
+ for (const subFile of readdirSync(sourcePath)) {
151
+ const subSource = join(sourcePath, subFile);
152
+ const subDest = join(destDir, subFile);
153
+ let content2 = readFileSync(subSource, "utf-8");
154
+ for (const [placeholder, value] of Object.entries(replacements)) {
155
+ content2 = content2.replace(new RegExp(`\\{\\{${placeholder}\\}\\}`, "g"), value);
156
+ }
157
+ writeFileSync2(subDest, content2);
158
+ }
159
+ continue;
160
+ }
161
+ if (isDir) {
162
+ continue;
163
+ }
164
+ const destFile = file === "gitignore" ? ".gitignore" : file;
165
+ const destPath = join(targetDir, destFile);
166
+ let content = readFileSync(sourcePath, "utf-8");
167
+ for (const [placeholder, value] of Object.entries(replacements)) {
168
+ content = content.replace(new RegExp(`\\{\\{${placeholder}\\}\\}`, "g"), value);
169
+ }
170
+ writeFileSync2(destPath, content);
171
+ }
172
+ }
173
+ async function bootstrapProject({ workspaceId, edsId, targetDir, projectName, apiKey }) {
174
+ if (!existsSync(targetDir)) {
175
+ mkdirSync2(targetDir, { recursive: true });
176
+ }
177
+ copyTemplates(targetDir, {
178
+ PROJECT_NAME: projectName
179
+ });
180
+ const edsResponse = await getEmailDesignSystem(workspaceId, edsId);
181
+ const edsData = edsResponse.data;
182
+ const componentsResponse = await getComponents(workspaceId, edsId);
183
+ const componentsData = componentsResponse.data;
184
+ const containerComponent = componentsData.results.find((c) => c.container);
185
+ const regularComponents = componentsData.results.filter((c) => !c.container);
186
+ if (!containerComponent) {
187
+ throw new Error("No container component found in the Email Design System");
188
+ }
189
+ if (existsSync(resolve2(targetDir, "src"))) {
190
+ rmSync(resolve2(targetDir, "src"), { recursive: true });
191
+ }
192
+ mkdirSync2(resolve2(targetDir, "src"), { recursive: true });
193
+ mkdirSync2(resolve2(targetDir, "src", "components"), { recursive: true });
194
+ mkdirSync2(resolve2(targetDir, "src", "container_component"), { recursive: true });
195
+ const viteEnvSource = join(TEMPLATES_DIR, "src", "vite-env.d.ts");
196
+ const viteEnvDest = resolve2(targetDir, "src", "vite-env.d.ts");
197
+ writeFileSync2(viteEnvDest, readFileSync(viteEnvSource, "utf-8"));
198
+ buildContainerComponentDirectory(containerComponent, targetDir);
199
+ for (const component of regularComponents) {
200
+ buildComponentDirectory(component, targetDir);
201
+ }
202
+ const componentsIndexContent = `import type { ComponentDefinition } from '@emailshepherd/cli/types';
203
+
204
+ const modules = import.meta.glob<{ default: ComponentDefinition }>('./*/index.ts', { eager: true });
205
+
206
+ export default Object.values(modules).map((m) => m.default);
207
+ `;
208
+ writeFileSync2(resolve2(targetDir, "src", "components", "index.ts"), componentsIndexContent);
209
+ const designTokensContent = `import { defineDesignTokens } from '@emailshepherd/cli/types';
210
+
211
+ export default defineDesignTokens(${JSON.stringify(edsData.design_tokens || {}, null, 2)});
212
+ `;
213
+ writeFileSync2(resolve2(targetDir, "src", "design_tokens.ts"), designTokensContent);
214
+ const customStylesContent = `import { defineCustomStyles } from '@emailshepherd/cli/types';
215
+
216
+ export default defineCustomStyles(${JSON.stringify(edsData.custom_styles || [], null, 2)});
217
+ `;
218
+ writeFileSync2(resolve2(targetDir, "src", "custom_styles.ts"), customStylesContent);
219
+ const edsContent = `import { defineEDS } from '@emailshepherd/cli/types';
220
+ import designTokens from './design_tokens';
221
+ import customStyles from './custom_styles';
222
+ import containerComponent from './container_component';
223
+ import components from './components';
224
+
225
+ export default defineEDS({
226
+ workspace_id: ${workspaceId},
227
+ email_design_system_id: ${edsId},
228
+ eds_metadata: {
229
+ name: ${JSON.stringify(edsData.name)},
230
+ design_tokens: designTokens,
231
+ custom_styles: customStyles,
232
+ },
233
+ container_component: containerComponent,
234
+ components,
235
+ });
236
+ `;
237
+ writeFileSync2(resolve2(targetDir, "src", "eds.ts"), edsContent);
238
+ const envContent = `EMAILSHEPHERD_API_KEY=${apiKey ?? "your_api_key_here"}
239
+ EMAILSHEPHERD_BASE_URL=${BASE_URL}
240
+ `;
241
+ writeFileSync2(resolve2(targetDir, ".env"), envContent);
242
+ console.log("Installing dependencies...");
243
+ execSync("npm install", { cwd: targetDir, stdio: "inherit" });
244
+ try {
245
+ execSync("git init", { cwd: targetDir, stdio: "ignore" });
246
+ execSync("git add .", { cwd: targetDir, stdio: "ignore" });
247
+ execSync('git commit -m "Initial commit from EmailShepherd CLI"', { cwd: targetDir, stdio: "ignore" });
248
+ } catch {
249
+ }
250
+ return { edsName: edsData.name };
251
+ }
252
+ function registerInitCommand(program2) {
253
+ program2.command("init").description("Interactively initialize a new Email Design System project").action(async () => {
254
+ try {
255
+ console.log("\n\u{1F4E7} Welcome to EmailShepherd CLI\n");
256
+ const apiKey = await password({
257
+ message: "Enter your EmailShepherd API Key:",
258
+ mask: "*"
259
+ });
260
+ configureAxios({ apiKey, baseUrl: BASE_URL });
261
+ const workspacesResponse = await getWorkspaces();
262
+ const workspaces = workspacesResponse.data.results;
263
+ if (workspaces.length === 0) {
264
+ console.error("No workspaces found for this API key.");
265
+ process.exit(1);
266
+ }
267
+ const workspaceId = await select({
268
+ message: "Select a workspace:",
269
+ choices: workspaces.map((ws) => ({
270
+ name: ws.name,
271
+ value: ws.id,
272
+ description: `ID: ${ws.id}`
273
+ }))
274
+ });
275
+ console.log("\nFetching email design systems...");
276
+ const edsResponse = await getEmailDesignSystems(workspaceId);
277
+ const emailDesignSystems = edsResponse.data.results;
278
+ if (emailDesignSystems.length === 0) {
279
+ console.error("No email design systems found in this workspace.");
280
+ process.exit(1);
281
+ }
282
+ const edsId = await select({
283
+ message: "Select an Email Design System:",
284
+ choices: emailDesignSystems.map((eds) => ({
285
+ name: eds.name,
286
+ value: eds.id,
287
+ description: eds.description || `ID: ${eds.id}`
288
+ }))
289
+ });
290
+ const selectedEds = emailDesignSystems.find((eds) => eds.id === edsId);
291
+ const suggestedDirName = selectedEds?.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
292
+ const dirName = await input({
293
+ message: "Enter the project directory name:",
294
+ default: suggestedDirName,
295
+ validate: (value) => {
296
+ if (!value.trim()) return "Directory name is required";
297
+ if (existsSync(resolve2(process.cwd(), value))) {
298
+ return `Directory "${value}" already exists. Choose a different name.`;
299
+ }
300
+ return true;
301
+ }
302
+ });
303
+ const targetDir = resolve2(process.cwd(), dirName);
304
+ console.log("\nBootstrapping your project...");
305
+ const { edsName } = await bootstrapProject({
306
+ workspaceId,
307
+ edsId,
308
+ targetDir,
309
+ projectName: dirName,
310
+ apiKey
311
+ });
312
+ console.log(`
313
+ \u2705 Project "${edsName}" initialized successfully!`);
314
+ console.log(`
315
+ Next steps:`);
316
+ console.log(` cd ${dirName}`);
317
+ console.log(` npx emailshepherd dev`);
318
+ console.log("");
319
+ } catch (error) {
320
+ if (error instanceof AxiosError && error.response?.data) {
321
+ console.error("\nAPI Error:", JSON.stringify(error.response.data, null, 2));
322
+ } else if (error instanceof Error) {
323
+ if (error.message.includes("User force closed")) {
324
+ console.log("\n\nSetup cancelled.");
325
+ process.exit(0);
326
+ }
327
+ console.error("\nError:", error.message);
328
+ } else {
329
+ console.error("\nError:", error);
330
+ }
331
+ process.exit(1);
332
+ }
333
+ });
334
+ }
335
+
336
+ // src/commands/validate.ts
337
+ import { AxiosError as AxiosError3 } from "axios";
338
+
339
+ // src/buildHtml.ts
340
+ import { AxiosError as AxiosError2 } from "axios";
341
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
342
+ import { resolve as resolve3 } from "path";
343
+ import { createServer } from "vite";
344
+ var viteServer = null;
345
+ async function getViteServer() {
346
+ if (!viteServer) {
347
+ viteServer = await createServer({
348
+ root: process.cwd(),
349
+ server: { middlewareMode: true },
350
+ appType: "custom",
351
+ logLevel: "silent"
352
+ });
353
+ }
354
+ return viteServer;
355
+ }
356
+ function renderErrorHtml(title, message, details) {
357
+ return `<!DOCTYPE html>
358
+ <html lang="en">
359
+ <head>
360
+ <meta charset="UTF-8" />
361
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
362
+ <title>Email Shepherd - Error</title>
363
+ <style>
364
+ body {
365
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
366
+ background: #1a1a1a;
367
+ color: #e0e0e0;
368
+ padding: 40px;
369
+ margin: 0;
370
+ }
371
+ .error-container {
372
+ max-width: 800px;
373
+ margin: 0 auto;
374
+ }
375
+ h1 {
376
+ color: #ff6b6b;
377
+ margin-bottom: 8px;
378
+ }
379
+ .status {
380
+ color: #888;
381
+ margin-bottom: 24px;
382
+ }
383
+ pre {
384
+ background: #2a2a2a;
385
+ border: 1px solid #444;
386
+ border-radius: 8px;
387
+ padding: 20px;
388
+ overflow-x: auto;
389
+ font-size: 14px;
390
+ line-height: 1.5;
391
+ }
392
+ </style>
393
+ </head>
394
+ <body>
395
+ <div class="error-container">
396
+ <h1>${title}</h1>
397
+ <p class="status">${message}</p>
398
+ <pre>${typeof details === "string" ? details : JSON.stringify(details, null, 2)}</pre>
399
+ </div>
400
+ </body>
401
+ </html>`;
402
+ }
403
+ var loadEDS = async () => {
404
+ const edsPath = resolve3(process.cwd(), "src", "eds.ts");
405
+ if (!existsSync2(edsPath)) {
406
+ throw new Error(`Missing src/eds.ts - this file defines your Email Design System configuration.`);
407
+ }
408
+ const server = await getViteServer();
409
+ const edsModule = await server.ssrLoadModule(edsPath);
410
+ return edsModule.default;
411
+ };
412
+ var buildEmailDesignSystemObject = async (eds) => {
413
+ return {
414
+ email_design_system: {
415
+ design_tokens: eds.eds_metadata.design_tokens,
416
+ custom_styles: eds.eds_metadata.custom_styles || []
417
+ },
418
+ container_component: eds.container_component,
419
+ components: eds.components
420
+ };
421
+ };
422
+ function writeHtml(html) {
423
+ const distDir = resolve3(process.cwd(), "dist");
424
+ mkdirSync3(distDir, { recursive: true });
425
+ writeFileSync3(resolve3(distDir, "index.html"), html);
426
+ }
427
+ async function buildHtml() {
428
+ configureAxios();
429
+ let html;
430
+ try {
431
+ const eds = await loadEDS();
432
+ const objectRepresentation = await buildEmailDesignSystemObject(eds);
433
+ const response = await renderEmailDesignSystem(
434
+ eds.workspace_id,
435
+ eds.email_design_system_id,
436
+ objectRepresentation
437
+ );
438
+ if (response.data.html) {
439
+ html = response.data.html;
440
+ } else {
441
+ html = renderErrorHtml("API Error", "No HTML returned", response.data);
442
+ }
443
+ } catch (error) {
444
+ if (error instanceof AxiosError2) {
445
+ const status = error.response?.status ?? "Unknown";
446
+ const statusText = error.response?.statusText ?? error.message;
447
+ html = renderErrorHtml("API Error", `Status: ${status} ${statusText}`, error.response?.data ?? error.message);
448
+ } else if (error instanceof Error) {
449
+ html = renderErrorHtml("Error", error.message, error.stack ?? "");
450
+ } else {
451
+ html = renderErrorHtml("Error", "Unknown error", error);
452
+ }
453
+ }
454
+ writeHtml(html);
455
+ return html;
456
+ }
457
+
458
+ // src/loadEDS.ts
459
+ import { build } from "vite";
460
+ import { existsSync as existsSync3 } from "fs";
461
+ import { resolve as resolve4 } from "path";
462
+ async function loadEDS2() {
463
+ const edsPath = resolve4(process.cwd(), "src", "eds.ts");
464
+ if (!existsSync3(edsPath)) {
465
+ throw new Error("Missing src/eds.ts - this file defines your Email Design System configuration.");
466
+ }
467
+ const result = await build({
468
+ root: process.cwd(),
469
+ logLevel: "silent",
470
+ build: {
471
+ write: false,
472
+ lib: {
473
+ entry: edsPath,
474
+ formats: ["es"]
475
+ }
476
+ }
477
+ });
478
+ const output = Array.isArray(result) ? result[0] : result;
479
+ const chunk = output.output.find((o) => o.type === "chunk");
480
+ if (!chunk) {
481
+ throw new Error("Failed to bundle EDS");
482
+ }
483
+ const dataUrl = `data:text/javascript;base64,${Buffer.from(chunk.code).toString("base64")}`;
484
+ const module = await import(dataUrl);
485
+ return module.default;
486
+ }
487
+
488
+ // src/commands/validate.ts
489
+ function registerValidateCommand(program2) {
490
+ program2.command("validate").description("Validate the Email Design System").action(async () => {
491
+ try {
492
+ configureAxios();
493
+ const eds = await loadEDS2();
494
+ const objectRepresentation = await buildEmailDesignSystemObject(eds);
495
+ const response = await renderEmailDesignSystem(
496
+ eds.workspace_id,
497
+ eds.email_design_system_id,
498
+ objectRepresentation
499
+ );
500
+ if (response.data.html) {
501
+ console.log("No validation errors");
502
+ } else {
503
+ console.log(JSON.stringify(response.data, null, 2));
504
+ process.exit(1);
505
+ }
506
+ } catch (error) {
507
+ if (error instanceof AxiosError3 && error.response?.data) {
508
+ console.log(JSON.stringify(error.response.data, null, 2));
509
+ } else if (error instanceof Error) {
510
+ console.error(error.message);
511
+ } else {
512
+ console.error(error);
513
+ }
514
+ process.exit(1);
515
+ }
516
+ });
517
+ }
518
+
519
+ // src/commands/save-component.ts
520
+ import { AxiosError as AxiosError4 } from "axios";
521
+ function registerSaveComponentCommand(program2) {
522
+ program2.command("save-component").description("Save a component to the server").option("--by-name <name>", "Component name to save").option("--by-id <id>", "Component ID to save").action(async (options) => {
523
+ try {
524
+ if (!options.byName && !options.byId) {
525
+ console.error("Error: Either --by-name or --by-id is required");
526
+ process.exit(1);
527
+ }
528
+ configureAxios();
529
+ const eds = await loadEDS2();
530
+ const component = eds.container_component.id === Number(options.byId) || eds.container_component.name === options.byName ? eds.container_component : eds.components.find(
531
+ (c) => options.byId ? c.id === Number(options.byId) : c.name === options.byName
532
+ );
533
+ if (!component) {
534
+ console.error(`Error: Component not found${options.byName ? ` with name "${options.byName}"` : ` with id ${options.byId}`}`);
535
+ process.exit(1);
536
+ }
537
+ const response = await updateComponent(
538
+ eds.workspace_id,
539
+ eds.email_design_system_id,
540
+ component.id,
541
+ component
542
+ );
543
+ console.log(`Component "${response.data.name}" saved successfully`);
544
+ } catch (error) {
545
+ if (error instanceof AxiosError4 && error.response?.data) {
546
+ console.log(JSON.stringify(error.response.data, null, 2));
547
+ } else if (error instanceof Error) {
548
+ console.error(error.message);
549
+ } else {
550
+ console.error(error);
551
+ }
552
+ process.exit(1);
553
+ }
554
+ });
555
+ }
556
+
557
+ // src/commands/save-eds.ts
558
+ import { AxiosError as AxiosError5 } from "axios";
559
+ function registerSaveEDSCommand(program2) {
560
+ program2.command("save-eds").description("Save the Email Design System metadata to the server").action(async () => {
561
+ try {
562
+ configureAxios();
563
+ const eds = await loadEDS2();
564
+ const response = await updateEmailDesignSystem(
565
+ eds.workspace_id,
566
+ eds.email_design_system_id,
567
+ {
568
+ name: eds.eds_metadata.name,
569
+ design_tokens: eds.eds_metadata.design_tokens,
570
+ custom_styles: eds.eds_metadata.custom_styles
571
+ }
572
+ );
573
+ console.log(`Email Design System "${response.data.name}" saved successfully`);
574
+ } catch (error) {
575
+ if (error instanceof AxiosError5 && error.response?.data) {
576
+ console.log(JSON.stringify(error.response.data, null, 2));
577
+ } else if (error instanceof Error) {
578
+ console.error(error.message);
579
+ } else {
580
+ console.error(error);
581
+ }
582
+ process.exit(1);
583
+ }
584
+ });
585
+ }
586
+
587
+ // src/commands/save-all.ts
588
+ import { AxiosError as AxiosError6 } from "axios";
589
+ function registerSaveAllCommand(program2) {
590
+ program2.command("save-all").description("Save the Email Design System and all components to the server").action(async () => {
591
+ try {
592
+ configureAxios();
593
+ const eds = await loadEDS2();
594
+ console.log("Saving Email Design System...");
595
+ const edsResponse = await updateEmailDesignSystem(
596
+ eds.workspace_id,
597
+ eds.email_design_system_id,
598
+ {
599
+ name: eds.eds_metadata.name,
600
+ design_tokens: eds.eds_metadata.design_tokens,
601
+ custom_styles: eds.eds_metadata.custom_styles
602
+ }
603
+ );
604
+ console.log(`Email Design System "${edsResponse.data.name}" saved successfully`);
605
+ console.log(`Saving container component "${eds.container_component.name}"...`);
606
+ const containerResponse = await updateComponent(
607
+ eds.workspace_id,
608
+ eds.email_design_system_id,
609
+ eds.container_component.id,
610
+ eds.container_component
611
+ );
612
+ console.log(`Container component "${containerResponse.data.name}" saved successfully`);
613
+ for (const component of eds.components) {
614
+ console.log(`Saving component "${component.name}"...`);
615
+ const response = await updateComponent(
616
+ eds.workspace_id,
617
+ eds.email_design_system_id,
618
+ component.id,
619
+ component
620
+ );
621
+ console.log(`Component "${response.data.name}" saved successfully`);
622
+ }
623
+ console.log("All saved successfully");
624
+ } catch (error) {
625
+ if (error instanceof AxiosError6 && error.response?.data) {
626
+ console.log(JSON.stringify(error.response.data, null, 2));
627
+ } else if (error instanceof Error) {
628
+ console.error(error.message);
629
+ } else {
630
+ console.error(error);
631
+ }
632
+ process.exit(1);
633
+ }
634
+ });
635
+ }
636
+
637
+ // src/commands/dev.ts
638
+ import { createServer as createServer2 } from "vite";
639
+ import chokidar from "chokidar";
640
+ function registerDevCommand(program2) {
641
+ program2.command("dev").description("Start the development server with hot reload").option("-p, --port <port>", "Port to run the server on", "5173").action(async (options) => {
642
+ const port = parseInt(options.port, 10);
643
+ console.log("Building...");
644
+ await buildHtml();
645
+ const server = await createServer2({
646
+ root: "dist",
647
+ server: { port }
648
+ });
649
+ const watcher = chokidar.watch("src", { ignoreInitial: true, usePolling: true, interval: 300 });
650
+ watcher.on("all", async (event, path) => {
651
+ console.log(`[src] ${event}: ${path}, rebuilding...`);
652
+ await buildHtml();
653
+ server.ws.send({ type: "full-reload" });
654
+ });
655
+ await server.listen();
656
+ server.printUrls();
657
+ });
658
+ }
659
+
660
+ // src/commands/create-component.ts
661
+ import { AxiosError as AxiosError7 } from "axios";
662
+ function registerCreateComponentCommand(program2) {
663
+ program2.command("create-component").description("Create a new component in the Email Design System").requiredOption("--name <name>", "Component name (used as directory name and in code)").requiredOption("--label <label>", "Component label (displayed in the UI)").action(async (options) => {
664
+ try {
665
+ configureAxios();
666
+ const eds = await loadEDS2();
667
+ const response = await createComponent(
668
+ eds.workspace_id,
669
+ eds.email_design_system_id,
670
+ {
671
+ name: options.name,
672
+ label: options.label,
673
+ template: "<div>Hello, world!</div>",
674
+ field_definitions: []
675
+ }
676
+ );
677
+ const component = response.data;
678
+ const targetDir = process.cwd();
679
+ buildComponentDirectory(component, targetDir);
680
+ console.log(`Component "${component.name}" created successfully`);
681
+ } catch (error) {
682
+ if (error instanceof AxiosError7 && error.response?.data) {
683
+ console.log(JSON.stringify(error.response.data, null, 2));
684
+ } else if (error instanceof Error) {
685
+ console.error(error.message);
686
+ } else {
687
+ console.error(error);
688
+ }
689
+ process.exit(1);
690
+ }
691
+ });
692
+ }
693
+
694
+ // src/cli.ts
695
+ var program = new Command();
696
+ program.name("emailshepherd").description("EmailShepherd CLI").version(version);
697
+ registerInitCommand(program);
698
+ registerValidateCommand(program);
699
+ registerSaveComponentCommand(program);
700
+ registerCreateComponentCommand(program);
701
+ registerSaveEDSCommand(program);
702
+ registerSaveAllCommand(program);
703
+ registerDevCommand(program);
704
+ program.parse();