@emailshepherd/cli 0.1.26 → 0.1.35
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.js +271 -467
- package/dist/templates/devWrapper.html +281 -0
- package/dist/types.d.ts +5 -289
- package/package.json +14 -10
- package/templates/AGENTS.md +0 -163
- package/templates/README.md +0 -115
- package/templates/claude/CLAUDE.md +0 -1
- package/templates/claude/settings.local.json +0 -8
- package/templates/gitignore +0 -6
- package/templates/package.json +0 -13
- package/templates/src/vite-env.d.ts +0 -6
- package/templates/tsconfig.json +0 -12
package/dist/cli.js
CHANGED
|
@@ -3,344 +3,68 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import "dotenv/config";
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
+
import updateNotifier from "update-notifier";
|
|
6
7
|
|
|
7
8
|
// package.json
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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);
|
|
9
|
+
var package_default = {
|
|
10
|
+
name: "@emailshepherd/cli",
|
|
11
|
+
version: "0.1.35",
|
|
12
|
+
type: "module",
|
|
13
|
+
publishConfig: {
|
|
14
|
+
registry: "https://registry.npmjs.org",
|
|
15
|
+
access: "public"
|
|
16
|
+
},
|
|
17
|
+
bin: {
|
|
18
|
+
emailshepherd: "./dist/cli.js"
|
|
19
|
+
},
|
|
20
|
+
exports: {
|
|
21
|
+
"./types": {
|
|
22
|
+
types: "./dist/types.d.ts",
|
|
23
|
+
default: "./dist/types.js"
|
|
169
24
|
}
|
|
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
25
|
},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
26
|
+
files: [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
scripts: {
|
|
30
|
+
build: "tsup && cp -r src/templates dist/",
|
|
31
|
+
test: "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
prepublishOnly: "pnpm run build"
|
|
34
|
+
},
|
|
35
|
+
dependencies: {
|
|
36
|
+
"@emailshepherd/api-client": "workspace:*",
|
|
37
|
+
axios: "^1.13.2",
|
|
38
|
+
chokidar: "^4.0.3",
|
|
39
|
+
commander: "^14.0.2",
|
|
40
|
+
dotenv: "^17.2.3",
|
|
41
|
+
"update-notifier": "^7.3.1",
|
|
42
|
+
vite: "^7.2.6",
|
|
43
|
+
"vite-plugin-checker": "^0.12.0"
|
|
44
|
+
},
|
|
45
|
+
devDependencies: {
|
|
46
|
+
"@types/node": "^24.10.1",
|
|
47
|
+
"@types/update-notifier": "^6.0.8",
|
|
48
|
+
msw: "^2.12.7",
|
|
49
|
+
tsup: "^8.5.1",
|
|
50
|
+
tsx: "^4.21.0",
|
|
51
|
+
typescript: "^5.9.3",
|
|
52
|
+
vitest: "^4.0.16"
|
|
249
53
|
}
|
|
250
|
-
|
|
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
|
-
}
|
|
54
|
+
};
|
|
335
55
|
|
|
336
56
|
// src/commands/validate.ts
|
|
337
|
-
import { AxiosError as
|
|
57
|
+
import { AxiosError as AxiosError2 } from "axios";
|
|
58
|
+
import { configureAxios as configureAxios2, renderEmailDesignSystem as renderEmailDesignSystem2 } from "@emailshepherd/api-client";
|
|
338
59
|
|
|
339
60
|
// src/buildHtml.ts
|
|
340
|
-
import { AxiosError
|
|
341
|
-
import { existsSync
|
|
342
|
-
import { resolve
|
|
61
|
+
import { AxiosError } from "axios";
|
|
62
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
63
|
+
import { dirname, resolve } from "path";
|
|
64
|
+
import { fileURLToPath } from "url";
|
|
343
65
|
import { createServer } from "vite";
|
|
66
|
+
import { configureAxios, renderEmailDesignSystem } from "@emailshepherd/api-client";
|
|
67
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
344
68
|
var viteServer = null;
|
|
345
69
|
async function getViteServer() {
|
|
346
70
|
if (!viteServer) {
|
|
@@ -353,6 +77,12 @@ async function getViteServer() {
|
|
|
353
77
|
}
|
|
354
78
|
return viteServer;
|
|
355
79
|
}
|
|
80
|
+
async function closeViteServer() {
|
|
81
|
+
if (viteServer) {
|
|
82
|
+
await viteServer.close();
|
|
83
|
+
viteServer = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
356
86
|
function renderErrorHtml(title, message, details) {
|
|
357
87
|
return `<!DOCTYPE html>
|
|
358
88
|
<html lang="en">
|
|
@@ -401,8 +131,8 @@ function renderErrorHtml(title, message, details) {
|
|
|
401
131
|
</html>`;
|
|
402
132
|
}
|
|
403
133
|
var loadEDS = async () => {
|
|
404
|
-
const edsPath =
|
|
405
|
-
if (!
|
|
134
|
+
const edsPath = resolve(process.cwd(), "src", "eds.ts");
|
|
135
|
+
if (!existsSync(edsPath)) {
|
|
406
136
|
throw new Error(`Missing src/eds.ts - this file defines your Email Design System configuration.`);
|
|
407
137
|
}
|
|
408
138
|
const server = await getViteServer();
|
|
@@ -419,10 +149,17 @@ var buildEmailDesignSystemObject = async (eds) => {
|
|
|
419
149
|
components: eds.components
|
|
420
150
|
};
|
|
421
151
|
};
|
|
422
|
-
function
|
|
423
|
-
const distDir =
|
|
424
|
-
|
|
425
|
-
|
|
152
|
+
function writeRenderedHtml(html) {
|
|
153
|
+
const distDir = resolve(process.cwd(), "dist");
|
|
154
|
+
mkdirSync(distDir, { recursive: true });
|
|
155
|
+
writeFileSync(resolve(distDir, "rendered.html"), html);
|
|
156
|
+
}
|
|
157
|
+
function writeDevWrapper() {
|
|
158
|
+
const distDir = resolve(process.cwd(), "dist");
|
|
159
|
+
mkdirSync(distDir, { recursive: true });
|
|
160
|
+
const templatePath = resolve(__dirname, "templates", "devWrapper.html");
|
|
161
|
+
const devWrapperHtml = readFileSync(templatePath, "utf-8");
|
|
162
|
+
writeFileSync(resolve(distDir, "index.html"), devWrapperHtml);
|
|
426
163
|
}
|
|
427
164
|
async function buildHtml() {
|
|
428
165
|
configureAxios();
|
|
@@ -441,7 +178,7 @@ async function buildHtml() {
|
|
|
441
178
|
html = renderErrorHtml("API Error", "No HTML returned", response.data);
|
|
442
179
|
}
|
|
443
180
|
} catch (error) {
|
|
444
|
-
if (error instanceof
|
|
181
|
+
if (error instanceof AxiosError) {
|
|
445
182
|
const status = error.response?.status ?? "Unknown";
|
|
446
183
|
const statusText = error.response?.statusText ?? error.message;
|
|
447
184
|
html = renderErrorHtml("API Error", `Status: ${status} ${statusText}`, error.response?.data ?? error.message);
|
|
@@ -451,17 +188,17 @@ async function buildHtml() {
|
|
|
451
188
|
html = renderErrorHtml("Error", "Unknown error", error);
|
|
452
189
|
}
|
|
453
190
|
}
|
|
454
|
-
|
|
191
|
+
writeRenderedHtml(html);
|
|
455
192
|
return html;
|
|
456
193
|
}
|
|
457
194
|
|
|
458
195
|
// src/loadEDS.ts
|
|
459
196
|
import { build } from "vite";
|
|
460
|
-
import { existsSync as
|
|
461
|
-
import { resolve as
|
|
197
|
+
import { existsSync as existsSync2 } from "fs";
|
|
198
|
+
import { resolve as resolve2 } from "path";
|
|
462
199
|
async function loadEDS2() {
|
|
463
|
-
const edsPath =
|
|
464
|
-
if (!
|
|
200
|
+
const edsPath = resolve2(process.cwd(), "src", "eds.ts");
|
|
201
|
+
if (!existsSync2(edsPath)) {
|
|
465
202
|
throw new Error("Missing src/eds.ts - this file defines your Email Design System configuration.");
|
|
466
203
|
}
|
|
467
204
|
const result = await build({
|
|
@@ -485,26 +222,51 @@ async function loadEDS2() {
|
|
|
485
222
|
return module.default;
|
|
486
223
|
}
|
|
487
224
|
|
|
225
|
+
// src/typecheck.ts
|
|
226
|
+
import { execSync } from "child_process";
|
|
227
|
+
function runTypeCheck() {
|
|
228
|
+
try {
|
|
229
|
+
execSync("npx tsc --noEmit", {
|
|
230
|
+
cwd: process.cwd(),
|
|
231
|
+
encoding: "utf-8",
|
|
232
|
+
stdio: "pipe"
|
|
233
|
+
});
|
|
234
|
+
return { success: true, output: "" };
|
|
235
|
+
} catch (error) {
|
|
236
|
+
const execError = error;
|
|
237
|
+
const output = execError.stdout || execError.stderr || "Type check failed";
|
|
238
|
+
return { success: false, output };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
488
242
|
// src/commands/validate.ts
|
|
489
243
|
function registerValidateCommand(program2) {
|
|
490
244
|
program2.command("validate").description("Validate the Email Design System").action(async () => {
|
|
491
245
|
try {
|
|
492
|
-
|
|
246
|
+
console.log("Checking types...");
|
|
247
|
+
const typeCheck = runTypeCheck();
|
|
248
|
+
if (!typeCheck.success) {
|
|
249
|
+
console.error("Type errors found:\n");
|
|
250
|
+
console.error(typeCheck.output);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
configureAxios2();
|
|
493
254
|
const eds = await loadEDS2();
|
|
494
255
|
const objectRepresentation = await buildEmailDesignSystemObject(eds);
|
|
495
|
-
const response = await
|
|
256
|
+
const response = await renderEmailDesignSystem2(
|
|
496
257
|
eds.workspace_id,
|
|
497
258
|
eds.email_design_system_id,
|
|
498
259
|
objectRepresentation
|
|
499
260
|
);
|
|
500
261
|
if (response.data.html) {
|
|
501
262
|
console.log("No validation errors");
|
|
263
|
+
process.exit(0);
|
|
502
264
|
} else {
|
|
503
265
|
console.log(JSON.stringify(response.data, null, 2));
|
|
504
266
|
process.exit(1);
|
|
505
267
|
}
|
|
506
268
|
} catch (error) {
|
|
507
|
-
if (error instanceof
|
|
269
|
+
if (error instanceof AxiosError2 && error.response?.data) {
|
|
508
270
|
console.log(JSON.stringify(error.response.data, null, 2));
|
|
509
271
|
} else if (error instanceof Error) {
|
|
510
272
|
console.error(error.message);
|
|
@@ -516,63 +278,40 @@ function registerValidateCommand(program2) {
|
|
|
516
278
|
});
|
|
517
279
|
}
|
|
518
280
|
|
|
519
|
-
// src/commands/save
|
|
520
|
-
import { AxiosError as
|
|
521
|
-
|
|
522
|
-
|
|
281
|
+
// src/commands/save.ts
|
|
282
|
+
import { AxiosError as AxiosError3 } from "axios";
|
|
283
|
+
import { configureAxios as configureAxios3, syncEmailDesignSystem } from "@emailshepherd/api-client";
|
|
284
|
+
function registerSaveAllCommand(program2) {
|
|
285
|
+
program2.command("save").description("Save the Email Design System and all components to the server").action(async () => {
|
|
523
286
|
try {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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}`}`);
|
|
287
|
+
console.log("Checking types...");
|
|
288
|
+
const typeCheck = runTypeCheck();
|
|
289
|
+
if (!typeCheck.success) {
|
|
290
|
+
console.error("Type errors found:\n");
|
|
291
|
+
console.error(typeCheck.output);
|
|
535
292
|
process.exit(1);
|
|
536
293
|
}
|
|
537
|
-
|
|
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();
|
|
294
|
+
configureAxios3();
|
|
563
295
|
const eds = await loadEDS2();
|
|
564
|
-
|
|
296
|
+
console.log("Saving Email Design System...");
|
|
297
|
+
const edsResponse = await syncEmailDesignSystem(
|
|
565
298
|
eds.workspace_id,
|
|
566
299
|
eds.email_design_system_id,
|
|
567
300
|
{
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
301
|
+
email_design_system: {
|
|
302
|
+
name: eds.eds_metadata.name,
|
|
303
|
+
description: eds.eds_metadata.description,
|
|
304
|
+
design_tokens: eds.eds_metadata.design_tokens,
|
|
305
|
+
custom_styles: eds.eds_metadata.custom_styles || []
|
|
306
|
+
},
|
|
307
|
+
container_component: eds.container_component,
|
|
308
|
+
components: eds.components
|
|
571
309
|
}
|
|
572
310
|
);
|
|
573
|
-
console.log(`Email Design System
|
|
311
|
+
console.log(`Email Design System saved successfully`);
|
|
312
|
+
process.exit(0);
|
|
574
313
|
} catch (error) {
|
|
575
|
-
if (error instanceof
|
|
314
|
+
if (error instanceof AxiosError3 && error.response?.data) {
|
|
576
315
|
console.log(JSON.stringify(error.response.data, null, 2));
|
|
577
316
|
} else if (error instanceof Error) {
|
|
578
317
|
console.error(error.message);
|
|
@@ -584,72 +323,141 @@ function registerSaveEDSCommand(program2) {
|
|
|
584
323
|
});
|
|
585
324
|
}
|
|
586
325
|
|
|
587
|
-
// src/commands/
|
|
588
|
-
import {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
326
|
+
// src/commands/dev.ts
|
|
327
|
+
import { createServer as createServer2 } from "vite";
|
|
328
|
+
import chokidar from "chokidar";
|
|
329
|
+
import checker from "vite-plugin-checker";
|
|
330
|
+
|
|
331
|
+
// src/devServer.ts
|
|
332
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
333
|
+
import { resolve as resolve3 } from "path";
|
|
334
|
+
import { AxiosError as AxiosError4 } from "axios";
|
|
335
|
+
import { configureAxios as configureAxios4, renderEmailDesignSystem as renderEmailDesignSystem3 } from "@emailshepherd/api-client";
|
|
336
|
+
function extractFieldSchema(field) {
|
|
337
|
+
const base = {
|
|
338
|
+
type: field.type,
|
|
339
|
+
label: field.label,
|
|
340
|
+
liquid_variable: field.liquid_variable,
|
|
341
|
+
default_value: field.default_value
|
|
342
|
+
};
|
|
343
|
+
if (field.type === "enum") {
|
|
344
|
+
return {
|
|
345
|
+
...base,
|
|
346
|
+
options: field.options.map((o) => ({ label: o.label, value: o.value }))
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
return base;
|
|
350
|
+
}
|
|
351
|
+
function extractComponentSchema(component) {
|
|
352
|
+
return {
|
|
353
|
+
name: component.name,
|
|
354
|
+
label: component.label,
|
|
355
|
+
fields: component.field_definitions.map(extractFieldSchema)
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function writeFieldsManifest(eds) {
|
|
359
|
+
const distDir = resolve3(process.cwd(), "dist");
|
|
360
|
+
mkdirSync2(distDir, { recursive: true });
|
|
361
|
+
const manifest = {
|
|
362
|
+
container_component: extractComponentSchema(eds.container_component),
|
|
363
|
+
components: eds.components.map(extractComponentSchema)
|
|
364
|
+
};
|
|
365
|
+
writeFileSync2(resolve3(distDir, "fields.json"), JSON.stringify(manifest, null, 2));
|
|
366
|
+
}
|
|
367
|
+
function applyOverrides(eds, overrides) {
|
|
368
|
+
const applyToComponent = (component) => {
|
|
369
|
+
const componentOverrides = overrides[component.name];
|
|
370
|
+
if (!componentOverrides) return component;
|
|
371
|
+
return {
|
|
372
|
+
...component,
|
|
373
|
+
field_definitions: component.field_definitions.map((field) => {
|
|
374
|
+
const override = componentOverrides[field.liquid_variable];
|
|
375
|
+
if (override !== void 0) {
|
|
376
|
+
return { ...field, default_value: override };
|
|
602
377
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
378
|
+
return field;
|
|
379
|
+
})
|
|
380
|
+
};
|
|
381
|
+
};
|
|
382
|
+
return {
|
|
383
|
+
...eds,
|
|
384
|
+
container_component: applyToComponent(eds.container_component),
|
|
385
|
+
components: eds.components.map(applyToComponent)
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
async function renderWithOverrides(overrides) {
|
|
389
|
+
configureAxios4();
|
|
390
|
+
const eds = await loadEDS();
|
|
391
|
+
const modifiedEds = applyOverrides(eds, overrides);
|
|
392
|
+
const objectRepresentation = await buildEmailDesignSystemObject(modifiedEds);
|
|
393
|
+
const response = await renderEmailDesignSystem3(
|
|
394
|
+
modifiedEds.workspace_id,
|
|
395
|
+
modifiedEds.email_design_system_id,
|
|
396
|
+
objectRepresentation
|
|
397
|
+
);
|
|
398
|
+
if (response.data.html) {
|
|
399
|
+
return response.data.html;
|
|
400
|
+
}
|
|
401
|
+
throw new Error("No HTML returned from API");
|
|
402
|
+
}
|
|
403
|
+
function renderApiPlugin() {
|
|
404
|
+
return {
|
|
405
|
+
name: "emailshepherd-render-api",
|
|
406
|
+
configureServer(server) {
|
|
407
|
+
server.middlewares.use(async (req, res, next) => {
|
|
408
|
+
if (req.url !== "/api/render" || req.method !== "POST") {
|
|
409
|
+
return next();
|
|
410
|
+
}
|
|
411
|
+
let body = "";
|
|
412
|
+
req.on("data", (chunk) => {
|
|
413
|
+
body += chunk.toString();
|
|
414
|
+
});
|
|
415
|
+
req.on("end", async () => {
|
|
416
|
+
try {
|
|
417
|
+
const overrides = JSON.parse(body);
|
|
418
|
+
const html = await renderWithOverrides(overrides);
|
|
419
|
+
res.setHeader("Content-Type", "application/json");
|
|
420
|
+
res.end(JSON.stringify({ html }));
|
|
421
|
+
} catch (error) {
|
|
422
|
+
res.statusCode = 500;
|
|
423
|
+
res.setHeader("Content-Type", "application/json");
|
|
424
|
+
if (error instanceof AxiosError4 && error.response?.data) {
|
|
425
|
+
res.end(JSON.stringify({ error: error.response.data }));
|
|
426
|
+
} else if (error instanceof Error) {
|
|
427
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
428
|
+
} else {
|
|
429
|
+
res.end(JSON.stringify({ error: "Unknown error" }));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
});
|
|
633
434
|
}
|
|
634
|
-
}
|
|
435
|
+
};
|
|
635
436
|
}
|
|
636
437
|
|
|
637
438
|
// src/commands/dev.ts
|
|
638
|
-
import { createServer as createServer2 } from "vite";
|
|
639
|
-
import chokidar from "chokidar";
|
|
640
439
|
function registerDevCommand(program2) {
|
|
641
440
|
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
441
|
const port = parseInt(options.port, 10);
|
|
643
442
|
console.log("Building...");
|
|
443
|
+
const eds = await loadEDS();
|
|
644
444
|
await buildHtml();
|
|
445
|
+
writeDevWrapper();
|
|
446
|
+
writeFieldsManifest(eds);
|
|
645
447
|
const server = await createServer2({
|
|
646
448
|
root: "dist",
|
|
647
|
-
server: { port }
|
|
449
|
+
server: { port },
|
|
450
|
+
plugins: [
|
|
451
|
+
checker({ typescript: true }),
|
|
452
|
+
renderApiPlugin()
|
|
453
|
+
]
|
|
648
454
|
});
|
|
649
455
|
const watcher = chokidar.watch("src", { ignoreInitial: true, usePolling: true, interval: 300 });
|
|
650
456
|
watcher.on("all", async (event, path) => {
|
|
651
457
|
console.log(`[src] ${event}: ${path}, rebuilding...`);
|
|
458
|
+
const updatedEds = await loadEDS();
|
|
652
459
|
await buildHtml();
|
|
460
|
+
writeFieldsManifest(updatedEds);
|
|
653
461
|
server.ws.send({ type: "full-reload" });
|
|
654
462
|
});
|
|
655
463
|
await server.listen();
|
|
@@ -657,31 +465,26 @@ function registerDevCommand(program2) {
|
|
|
657
465
|
});
|
|
658
466
|
}
|
|
659
467
|
|
|
660
|
-
// src/commands/
|
|
661
|
-
import {
|
|
662
|
-
function
|
|
663
|
-
program2.command("
|
|
468
|
+
// src/commands/build.ts
|
|
469
|
+
import { resolve as resolve4 } from "path";
|
|
470
|
+
function registerBuildCommand(program2) {
|
|
471
|
+
program2.command("build").description("Build the Email Design System HTML output").action(async () => {
|
|
664
472
|
try {
|
|
665
|
-
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
);
|
|
677
|
-
|
|
678
|
-
const targetDir = process.cwd();
|
|
679
|
-
buildComponentDirectory(component, targetDir);
|
|
680
|
-
console.log(`Component "${component.name}" created successfully`);
|
|
473
|
+
console.log("Checking types...");
|
|
474
|
+
const typeCheck = runTypeCheck();
|
|
475
|
+
if (!typeCheck.success) {
|
|
476
|
+
console.error("Type errors found:\n");
|
|
477
|
+
console.error(typeCheck.output);
|
|
478
|
+
process.exit(1);
|
|
479
|
+
}
|
|
480
|
+
console.log("Building...");
|
|
481
|
+
await buildHtml();
|
|
482
|
+
await closeViteServer();
|
|
483
|
+
const outputPath = resolve4(process.cwd(), "dist", "rendered.html");
|
|
484
|
+
console.log(`Built successfully: ${outputPath}`);
|
|
485
|
+
process.exit(0);
|
|
681
486
|
} catch (error) {
|
|
682
|
-
if (error instanceof
|
|
683
|
-
console.log(JSON.stringify(error.response.data, null, 2));
|
|
684
|
-
} else if (error instanceof Error) {
|
|
487
|
+
if (error instanceof Error) {
|
|
685
488
|
console.error(error.message);
|
|
686
489
|
} else {
|
|
687
490
|
console.error(error);
|
|
@@ -692,13 +495,14 @@ function registerCreateComponentCommand(program2) {
|
|
|
692
495
|
}
|
|
693
496
|
|
|
694
497
|
// src/cli.ts
|
|
498
|
+
updateNotifier({ pkg: package_default }).notify({
|
|
499
|
+
message: `Update available: {currentVersion} \u2192 {latestVersion}
|
|
500
|
+
Run: npm update @emailshepherd/cli`
|
|
501
|
+
});
|
|
695
502
|
var program = new Command();
|
|
696
|
-
program.name("emailshepherd").description("EmailShepherd CLI").version(version);
|
|
697
|
-
registerInitCommand(program);
|
|
503
|
+
program.name("emailshepherd").description("EmailShepherd CLI - run commands from within an EDS project").version(package_default.version);
|
|
698
504
|
registerValidateCommand(program);
|
|
699
|
-
registerSaveComponentCommand(program);
|
|
700
|
-
registerCreateComponentCommand(program);
|
|
701
|
-
registerSaveEDSCommand(program);
|
|
702
505
|
registerSaveAllCommand(program);
|
|
703
506
|
registerDevCommand(program);
|
|
507
|
+
registerBuildCommand(program);
|
|
704
508
|
program.parse();
|