@adidas/htmplar-cli 0.0.0 → 2.0.0-alpha.1

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/bin.mjs ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import './chunk-VSA5JOPE.mjs';
3
+ //# sourceMappingURL=bin.mjs.map
4
+ //# sourceMappingURL=bin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"bin.mjs"}
@@ -0,0 +1,3 @@
1
+ export { buildEmails } from './chunk-3B2SEUWU.mjs';
2
+ //# sourceMappingURL=build-P2H7OT3Y.mjs.map
3
+ //# sourceMappingURL=build-P2H7OT3Y.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"build-P2H7OT3Y.mjs"}
@@ -0,0 +1,87 @@
1
+ import { rm, mkdir, writeFile, readdir } from 'fs/promises';
2
+ import { join, basename, extname } from 'path';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+
6
+ // src/commands/build.ts
7
+ async function buildEmails(options) {
8
+ const spinner = ora("Finding email files...").start();
9
+ const cwd = process.cwd();
10
+ const outputDir = join(cwd, options.output);
11
+ try {
12
+ if (options.clean) {
13
+ spinner.text = "Cleaning output directory...";
14
+ try {
15
+ await rm(outputDir, { recursive: true, force: true });
16
+ } catch {
17
+ }
18
+ }
19
+ spinner.text = "Creating output directory...";
20
+ await mkdir(outputDir, { recursive: true });
21
+ spinner.text = "Scanning for email files...";
22
+ const srcDir = join(cwd, "src");
23
+ const files = await findEmailFiles(srcDir);
24
+ if (files.length === 0) {
25
+ spinner.warn("No email files found in src/");
26
+ console.log(chalk.yellow(" Create email files in src/ with .tsx or .jsx extension"));
27
+ return;
28
+ }
29
+ spinner.succeed(`Found ${files.length} email file(s)`);
30
+ console.log("");
31
+ let successCount = 0;
32
+ let failCount = 0;
33
+ for (const file of files) {
34
+ const name = basename(file, extname(file));
35
+ const buildSpinner = ora(`Building ${name}...`).start();
36
+ try {
37
+ const { renderEmailFromFile } = await import('./renderEmail-WCOTFX4J.mjs');
38
+ const html = await renderEmailFromFile(file);
39
+ const outputPath = join(outputDir, `${name}.html`);
40
+ await writeFile(outputPath, html, "utf-8");
41
+ buildSpinner.succeed(chalk.green(`${name}.html`));
42
+ successCount++;
43
+ } catch (error) {
44
+ buildSpinner.fail(chalk.red(`Failed to build ${name}`));
45
+ if (error instanceof Error) {
46
+ console.error(chalk.gray(` ${error.message}`));
47
+ }
48
+ failCount++;
49
+ }
50
+ }
51
+ console.log("");
52
+ if (successCount > 0) {
53
+ console.log(chalk.green(`\u2705 Built ${successCount} email(s) to ${options.output}/`));
54
+ }
55
+ if (failCount > 0) {
56
+ console.log(chalk.yellow(`\u26A0\uFE0F ${failCount} email(s) failed to build`));
57
+ process.exit(1);
58
+ }
59
+ } catch (error) {
60
+ spinner.fail("Build failed");
61
+ throw error;
62
+ }
63
+ }
64
+ async function findEmailFiles(dir) {
65
+ const files = [];
66
+ try {
67
+ const entries = await readdir(dir, { withFileTypes: true });
68
+ for (const entry of entries) {
69
+ const fullPath = join(dir, entry.name);
70
+ if (entry.isDirectory()) {
71
+ const subFiles = await findEmailFiles(fullPath);
72
+ files.push(...subFiles);
73
+ } else if (entry.isFile()) {
74
+ const ext = extname(entry.name);
75
+ if (ext === ".tsx" || ext === ".jsx") {
76
+ files.push(fullPath);
77
+ }
78
+ }
79
+ }
80
+ } catch {
81
+ }
82
+ return files;
83
+ }
84
+
85
+ export { buildEmails };
86
+ //# sourceMappingURL=chunk-3B2SEUWU.mjs.map
87
+ //# sourceMappingURL=chunk-3B2SEUWU.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/build.ts"],"names":[],"mappings":"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","file":"chunk-3B2SEUWU.mjs","sourcesContent":["import { readdir, mkdir, writeFile, rm } from 'fs/promises';\nimport { join, extname, basename } from 'path';\nimport chalk from 'chalk';\nimport ora from 'ora';\n\ninterface BuildOptions {\n output: string;\n clean: boolean;\n}\n\nexport async function buildEmails(options: BuildOptions) {\n const spinner = ora('Finding email files...').start();\n const cwd = process.cwd();\n const outputDir = join(cwd, options.output);\n\n try {\n // Clean output directory if requested\n if (options.clean) {\n spinner.text = 'Cleaning output directory...';\n try {\n await rm(outputDir, { recursive: true, force: true });\n } catch {\n // Directory might not exist, that's okay\n }\n }\n\n // Create output directory\n spinner.text = 'Creating output directory...';\n await mkdir(outputDir, { recursive: true });\n\n // Find all email files (*.tsx, *.jsx)\n spinner.text = 'Scanning for email files...';\n const srcDir = join(cwd, 'src');\n const files = await findEmailFiles(srcDir);\n\n if (files.length === 0) {\n spinner.warn('No email files found in src/');\n console.log(chalk.yellow(' Create email files in src/ with .tsx or .jsx extension'));\n return;\n }\n\n spinner.succeed(`Found ${files.length} email file(s)`);\n\n // Build each email\n console.log('');\n let successCount = 0;\n let failCount = 0;\n\n for (const file of files) {\n const name = basename(file, extname(file));\n const buildSpinner = ora(`Building ${name}...`).start();\n\n try {\n const { renderEmailFromFile } = await import('../utils/renderEmail.js');\n const html = await renderEmailFromFile(file);\n\n const outputPath = join(outputDir, `${name}.html`);\n await writeFile(outputPath, html, 'utf-8');\n\n buildSpinner.succeed(chalk.green(`${name}.html`));\n successCount++;\n } catch (error) {\n buildSpinner.fail(chalk.red(`Failed to build ${name}`));\n if (error instanceof Error) {\n console.error(chalk.gray(` ${error.message}`));\n }\n failCount++;\n }\n }\n\n console.log('');\n if (successCount > 0) {\n console.log(chalk.green(`✅ Built ${successCount} email(s) to ${options.output}/`));\n }\n if (failCount > 0) {\n console.log(chalk.yellow(`⚠️ ${failCount} email(s) failed to build`));\n process.exit(1);\n }\n } catch (error) {\n spinner.fail('Build failed');\n throw error;\n }\n}\n\nasync function findEmailFiles(dir: string): Promise<string[]> {\n const files: string[] = [];\n\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // Recursively search subdirectories\n const subFiles = await findEmailFiles(fullPath);\n files.push(...subFiles);\n } else if (entry.isFile()) {\n const ext = extname(entry.name);\n if (ext === '.tsx' || ext === '.jsx') {\n files.push(fullPath);\n }\n }\n }\n } catch {\n // Directory might not exist\n }\n\n return files;\n}\n"]}
@@ -0,0 +1,107 @@
1
+ import { mkdir, writeFile } from 'fs/promises';
2
+ import { join } from 'path';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+
6
+ // src/commands/init.ts
7
+ async function initProject(name) {
8
+ const projectName = name || "my-emails";
9
+ const spinner = ora(`Creating project: ${projectName}`).start();
10
+ try {
11
+ const projectDir = join(process.cwd(), projectName);
12
+ await mkdir(join(projectDir, "src"), { recursive: true });
13
+ await mkdir(join(projectDir, "dist"), { recursive: true });
14
+ const packageJson = {
15
+ name: projectName,
16
+ version: "1.0.0",
17
+ private: true,
18
+ scripts: {
19
+ dev: "htmplar dev",
20
+ build: "htmplar build",
21
+ preview: "htmplar preview"
22
+ },
23
+ dependencies: {
24
+ "@adidas/htmplar-core": "^2.0.0-alpha.1",
25
+ "@adidas/htmplar-renderer": "^2.0.0-alpha.1",
26
+ react: "^18.3.1",
27
+ "react-dom": "^18.3.1"
28
+ },
29
+ devDependencies: {
30
+ "@adidas/htmplar-cli": "^2.0.0-alpha.1",
31
+ "@types/react": "^18.3.8",
32
+ "@types/react-dom": "^18.3.0",
33
+ typescript: "^5.6.2"
34
+ }
35
+ };
36
+ await writeFile(join(projectDir, "package.json"), JSON.stringify(packageJson, null, 2));
37
+ const exampleEmail = `import React from 'react';
38
+ import { Block, Text, Button } from '@adidas/htmplar-core';
39
+
40
+ export default function WelcomeEmail() {
41
+ return (
42
+ <Block backgroundColor="#f4f4f4" padding={40}>
43
+ <Block backgroundColor="#ffffff" padding={30} maxWidth={600}>
44
+ <Text fontSize={24} fontWeight="bold" align="center">
45
+ Welcome to HTMplar!
46
+ </Text>
47
+
48
+ <Block padding={20}>
49
+ <Text fontSize={16} color="#666666">
50
+ Start building amazing emails with React and TypeScript.
51
+ </Text>
52
+ </Block>
53
+
54
+ <Block padding={20}>
55
+ <Button href="https://github.com/adidas/htmplar">
56
+ Get Started
57
+ </Button>
58
+ </Block>
59
+ </Block>
60
+ </Block>
61
+ );
62
+ }
63
+ `;
64
+ await writeFile(join(projectDir, "src", "welcome.tsx"), exampleEmail);
65
+ const readme = `# ${projectName}
66
+
67
+ HTMplar email project
68
+
69
+ ## Getting Started
70
+
71
+ \`\`\`bash
72
+ # Install dependencies
73
+ npm install
74
+
75
+ # Start dev server
76
+ npm run dev
77
+
78
+ # Build emails
79
+ npm run build
80
+
81
+ # Preview built emails
82
+ npm run preview
83
+ \`\`\`
84
+
85
+ ## Project Structure
86
+
87
+ - \`src/\` - Email source files (React components)
88
+ - \`dist/\` - Built HTML files
89
+ `;
90
+ await writeFile(join(projectDir, "README.md"), readme);
91
+ spinner.succeed(chalk.green("Project created!"));
92
+ console.log("");
93
+ console.log(chalk.cyan("Next steps:"));
94
+ console.log("");
95
+ console.log(` cd ${projectName}`);
96
+ console.log(" npm install");
97
+ console.log(" npm run dev");
98
+ console.log("");
99
+ } catch (error) {
100
+ spinner.fail("Failed to create project");
101
+ throw error;
102
+ }
103
+ }
104
+
105
+ export { initProject };
106
+ //# sourceMappingURL=chunk-AW7RBGLV.mjs.map
107
+ //# sourceMappingURL=chunk-AW7RBGLV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/init.ts"],"names":[],"mappings":";;;;;;AAKA,eAAsB,YAAY,IAAA,EAAe;AAC/C,EAAA,MAAM,cAAc,IAAA,IAAQ,WAAA;AAC5B,EAAA,MAAM,UAAU,GAAA,CAAI,CAAA,kBAAA,EAAqB,WAAW,CAAA,CAAE,EAAE,KAAA,EAAM;AAE9D,EAAA,IAAI;AACF,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,OAAA,CAAQ,GAAA,IAAO,WAAW,CAAA;AAGlD,IAAA,MAAM,KAAA,CAAM,KAAK,UAAA,EAAY,KAAK,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACxD,IAAA,MAAM,KAAA,CAAM,KAAK,UAAA,EAAY,MAAM,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAGzD,IAAA,MAAM,WAAA,GAAc;AAAA,MAClB,IAAA,EAAM,WAAA;AAAA,MACN,OAAA,EAAS,OAAA;AAAA,MACT,OAAA,EAAS,IAAA;AAAA,MACT,OAAA,EAAS;AAAA,QACP,GAAA,EAAK,aAAA;AAAA,QACL,KAAA,EAAO,eAAA;AAAA,QACP,OAAA,EAAS;AAAA,OACX;AAAA,MACA,YAAA,EAAc;AAAA,QACZ,sBAAA,EAAwB,gBAAA;AAAA,QACxB,0BAAA,EAA4B,gBAAA;AAAA,QAC5B,KAAA,EAAO,SAAA;AAAA,QACP,WAAA,EAAa;AAAA,OACf;AAAA,MACA,eAAA,EAAiB;AAAA,QACf,qBAAA,EAAuB,gBAAA;AAAA,QACvB,cAAA,EAAgB,SAAA;AAAA,QAChB,kBAAA,EAAoB,SAAA;AAAA,QACpB,UAAA,EAAY;AAAA;AACd,KACF;AAEA,IAAA,MAAM,SAAA,CAAU,IAAA,CAAK,UAAA,EAAY,cAAc,CAAA,EAAG,KAAK,SAAA,CAAU,WAAA,EAAa,IAAA,EAAM,CAAC,CAAC,CAAA;AAGtF,IAAA,MAAM,YAAA,GAAe,CAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AA4BrB,IAAA,MAAM,UAAU,IAAA,CAAK,UAAA,EAAY,KAAA,EAAO,aAAa,GAAG,YAAY,CAAA;AAGpE,IAAA,MAAM,MAAA,GAAS,KAAK,WAAW;;AAAA;;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA,CAAA;AA0B/B,IAAA,MAAM,SAAA,CAAU,IAAA,CAAK,UAAA,EAAY,WAAW,GAAG,MAAM,CAAA;AAErD,IAAA,OAAA,CAAQ,OAAA,CAAQ,KAAA,CAAM,KAAA,CAAM,kBAAkB,CAAC,CAAA;AAE/C,IAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,IAAA,CAAK,aAAa,CAAC,CAAA;AACrC,IAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,KAAA,EAAQ,WAAW,CAAA,CAAE,CAAA;AACjC,IAAA,OAAA,CAAQ,IAAI,eAAe,CAAA;AAC3B,IAAA,OAAA,CAAQ,IAAI,eAAe,CAAA;AAC3B,IAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AAAA,EAChB,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAK,0BAA0B,CAAA;AACvC,IAAA,MAAM,KAAA;AAAA,EACR;AACF","file":"chunk-AW7RBGLV.mjs","sourcesContent":["import { mkdir, writeFile } from 'fs/promises';\nimport { join } from 'path';\nimport chalk from 'chalk';\nimport ora from 'ora';\n\nexport async function initProject(name?: string) {\n const projectName = name || 'my-emails';\n const spinner = ora(`Creating project: ${projectName}`).start();\n\n try {\n const projectDir = join(process.cwd(), projectName);\n\n // Create directories\n await mkdir(join(projectDir, 'src'), { recursive: true });\n await mkdir(join(projectDir, 'dist'), { recursive: true });\n\n // Create package.json\n const packageJson = {\n name: projectName,\n version: '1.0.0',\n private: true,\n scripts: {\n dev: 'htmplar dev',\n build: 'htmplar build',\n preview: 'htmplar preview',\n },\n dependencies: {\n '@adidas/htmplar-core': '^2.0.0-alpha.1',\n '@adidas/htmplar-renderer': '^2.0.0-alpha.1',\n react: '^18.3.1',\n 'react-dom': '^18.3.1',\n },\n devDependencies: {\n '@adidas/htmplar-cli': '^2.0.0-alpha.1',\n '@types/react': '^18.3.8',\n '@types/react-dom': '^18.3.0',\n typescript: '^5.6.2',\n },\n };\n\n await writeFile(join(projectDir, 'package.json'), JSON.stringify(packageJson, null, 2));\n\n // Create example email\n const exampleEmail = `import React from 'react';\nimport { Block, Text, Button } from '@adidas/htmplar-core';\n\nexport default function WelcomeEmail() {\n return (\n <Block backgroundColor=\"#f4f4f4\" padding={40}>\n <Block backgroundColor=\"#ffffff\" padding={30} maxWidth={600}>\n <Text fontSize={24} fontWeight=\"bold\" align=\"center\">\n Welcome to HTMplar!\n </Text>\n\n <Block padding={20}>\n <Text fontSize={16} color=\"#666666\">\n Start building amazing emails with React and TypeScript.\n </Text>\n </Block>\n\n <Block padding={20}>\n <Button href=\"https://github.com/adidas/htmplar\">\n Get Started\n </Button>\n </Block>\n </Block>\n </Block>\n );\n}\n`;\n\n await writeFile(join(projectDir, 'src', 'welcome.tsx'), exampleEmail);\n\n // Create README\n const readme = `# ${projectName}\n\nHTMplar email project\n\n## Getting Started\n\n\\`\\`\\`bash\n# Install dependencies\nnpm install\n\n# Start dev server\nnpm run dev\n\n# Build emails\nnpm run build\n\n# Preview built emails\nnpm run preview\n\\`\\`\\`\n\n## Project Structure\n\n- \\`src/\\` - Email source files (React components)\n- \\`dist/\\` - Built HTML files\n`;\n\n await writeFile(join(projectDir, 'README.md'), readme);\n\n spinner.succeed(chalk.green('Project created!'));\n\n console.log('');\n console.log(chalk.cyan('Next steps:'));\n console.log('');\n console.log(` cd ${projectName}`);\n console.log(' npm install');\n console.log(' npm run dev');\n console.log('');\n } catch (error) {\n spinner.fail('Failed to create project');\n throw error;\n }\n}\n"]}
@@ -0,0 +1,161 @@
1
+ import { createServer } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import chalk from 'chalk';
4
+ import path from 'path';
5
+ import { readdir } from 'fs/promises';
6
+
7
+ // src/commands/dev.ts
8
+ async function discoverEmailTemplates() {
9
+ const srcDir = path.join(process.cwd(), "src");
10
+ try {
11
+ const files = await readdir(srcDir);
12
+ return files.filter(
13
+ (file) => (file.endsWith(".tsx") || file.endsWith(".jsx")) && file !== "main.tsx" && file !== "main.jsx"
14
+ ).map((file) => file.replace(/\.(tsx|jsx)$/, ""));
15
+ } catch {
16
+ return [];
17
+ }
18
+ }
19
+ function createRoutingPlugin(templates) {
20
+ return {
21
+ name: "htmplar-routing",
22
+ configureServer(server) {
23
+ server.middlewares.use(async (req, res, next) => {
24
+ const url = req.url || "";
25
+ if (url === "/" || url === "/index.html") {
26
+ const templateLinks = templates.map(
27
+ (name) => `<li style="margin: 10px 0;"><a href="/${name}" style="color: #0066cc; text-decoration: none; font-size: 16px;">\u{1F4E7} ${name}</a></li>`
28
+ ).join("");
29
+ const html = `<!DOCTYPE html>
30
+ <html lang="en">
31
+ <head>
32
+ <meta charset="UTF-8" />
33
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
34
+ <title>HTMplar Email Templates</title>
35
+ <style>
36
+ body {
37
+ font-family: system-ui, -apple-system, sans-serif;
38
+ max-width: 800px;
39
+ margin: 0 auto;
40
+ padding: 40px 20px;
41
+ background: #f9f9f9;
42
+ }
43
+ h1 { color: #333; }
44
+ ul { list-style: none; padding: 0; }
45
+ a:hover { text-decoration: underline !important; }
46
+ .card {
47
+ background: white;
48
+ padding: 30px;
49
+ border-radius: 8px;
50
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
51
+ }
52
+ </style>
53
+ </head>
54
+ <body>
55
+ <div class="card">
56
+ <h1>\u{1F4E7} HTMplar Email Templates</h1>
57
+ <p style="color: #666; margin-top: 10px;">
58
+ Click on a template below to preview it. Changes will hot reload automatically.
59
+ </p>
60
+ ${templates.length > 0 ? `<ul style="margin-top: 30px;">${templateLinks}</ul>` : '<p style="color: #999; margin-top: 30px;">No email templates found. Create a .tsx file in the src/ directory.</p>'}
61
+ </div>
62
+ </body>
63
+ </html>`;
64
+ res.setHeader("Content-Type", "text/html");
65
+ res.end(html);
66
+ return;
67
+ }
68
+ const routeMatch = url.match(/^\/([^/?]+)/);
69
+ if (routeMatch) {
70
+ const templateName = routeMatch[1];
71
+ if (templates.includes(templateName)) {
72
+ const ext = await getTemplateExtension(templateName);
73
+ const html = `<!DOCTYPE html>
74
+ <html lang="en">
75
+ <head>
76
+ <meta charset="UTF-8" />
77
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
78
+ <title>${templateName} - HTMplar</title>
79
+ <style>
80
+ body { margin: 0; padding: 0; }
81
+ #root { min-height: 100vh; }
82
+ </style>
83
+ </head>
84
+ <body>
85
+ <div id="root"></div>
86
+ <script type="module">
87
+ import React from 'react';
88
+ import { createRoot } from 'react-dom/client';
89
+ import EmailComponent from '/src/${templateName}.${ext}';
90
+
91
+ const root = createRoot(document.getElementById('root'));
92
+ root.render(React.createElement(EmailComponent));
93
+ </script>
94
+ </body>
95
+ </html>`;
96
+ res.setHeader("Content-Type", "text/html");
97
+ res.end(html);
98
+ return;
99
+ }
100
+ }
101
+ next();
102
+ });
103
+ }
104
+ };
105
+ }
106
+ async function getTemplateExtension(name) {
107
+ const srcDir = path.join(process.cwd(), "src");
108
+ const files = await readdir(srcDir);
109
+ const file = files.find((f) => f === `${name}.tsx` || f === `${name}.jsx`);
110
+ return file?.endsWith(".tsx") ? "tsx" : "jsx";
111
+ }
112
+ async function startDevServer(options) {
113
+ const port = parseInt(options.port, 10);
114
+ try {
115
+ process.env.VITE_TELEMETRY_DISABLED = "1";
116
+ const templates = await discoverEmailTemplates();
117
+ const server = await createServer({
118
+ configFile: false,
119
+ root: process.cwd(),
120
+ server: {
121
+ port,
122
+ open: options.open
123
+ },
124
+ plugins: [react(), createRoutingPlugin(templates)],
125
+ resolve: {
126
+ alias: {
127
+ "@adidas/htmplar-core": path.resolve(process.cwd(), "node_modules/@adidas/htmplar-core"),
128
+ "@adidas/htmplar-renderer": path.resolve(
129
+ process.cwd(),
130
+ "node_modules/@adidas/htmplar-renderer"
131
+ )
132
+ }
133
+ }
134
+ });
135
+ await server.listen();
136
+ console.log("");
137
+ console.log(chalk.green("\u2705 Dev server running!"));
138
+ console.log("");
139
+ console.log(chalk.cyan(` \u279C Local: http://localhost:${port}/`));
140
+ console.log("");
141
+ console.log(chalk.gray(" Press Ctrl+C to stop"));
142
+ console.log("");
143
+ process.on("SIGTERM", async () => {
144
+ await server.close();
145
+ process.exit(0);
146
+ });
147
+ process.on("SIGINT", async () => {
148
+ console.log("");
149
+ console.log(chalk.yellow("Stopping dev server..."));
150
+ await server.close();
151
+ process.exit(0);
152
+ });
153
+ } catch (error) {
154
+ console.error(chalk.red("Failed to start dev server:"), error);
155
+ throw error;
156
+ }
157
+ }
158
+
159
+ export { startDevServer };
160
+ //# sourceMappingURL=chunk-PDVVYV5V.mjs.map
161
+ //# sourceMappingURL=chunk-PDVVYV5V.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/dev.ts"],"names":[],"mappings":"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","file":"chunk-PDVVYV5V.mjs","sourcesContent":["import { createServer, ViteDevServer } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport chalk from 'chalk';\nimport path from 'path';\nimport { readdir } from 'fs/promises';\n\ninterface DevOptions {\n port: string;\n open: boolean;\n}\n\nasync function discoverEmailTemplates(): Promise<string[]> {\n const srcDir = path.join(process.cwd(), 'src');\n try {\n const files = await readdir(srcDir);\n return files\n .filter(\n (file) =>\n (file.endsWith('.tsx') || file.endsWith('.jsx')) &&\n file !== 'main.tsx' &&\n file !== 'main.jsx'\n )\n .map((file) => file.replace(/\\.(tsx|jsx)$/, ''));\n } catch {\n return [];\n }\n}\n\nfunction createRoutingPlugin(templates: string[]) {\n return {\n name: 'htmplar-routing',\n configureServer(server: ViteDevServer) {\n server.middlewares.use(async (req, res, next) => {\n const url = req.url || '';\n\n // Root - show index with list of templates\n if (url === '/' || url === '/index.html') {\n const templateLinks = templates\n .map(\n (name) =>\n `<li style=\"margin: 10px 0;\"><a href=\"/${name}\" style=\"color: #0066cc; text-decoration: none; font-size: 16px;\">📧 ${name}</a></li>`\n )\n .join('');\n\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>HTMplar Email Templates</title>\n <style>\n body {\n font-family: system-ui, -apple-system, sans-serif;\n max-width: 800px;\n margin: 0 auto;\n padding: 40px 20px;\n background: #f9f9f9;\n }\n h1 { color: #333; }\n ul { list-style: none; padding: 0; }\n a:hover { text-decoration: underline !important; }\n .card {\n background: white;\n padding: 30px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n }\n </style>\n </head>\n <body>\n <div class=\"card\">\n <h1>📧 HTMplar Email Templates</h1>\n <p style=\"color: #666; margin-top: 10px;\">\n Click on a template below to preview it. Changes will hot reload automatically.\n </p>\n ${\n templates.length > 0\n ? `<ul style=\"margin-top: 30px;\">${templateLinks}</ul>`\n : '<p style=\"color: #999; margin-top: 30px;\">No email templates found. Create a .tsx file in the src/ directory.</p>'\n }\n </div>\n </body>\n</html>`;\n\n res.setHeader('Content-Type', 'text/html');\n res.end(html);\n return;\n }\n\n // Template routes - serve dynamic preview\n const routeMatch = url.match(/^\\/([^/?]+)/);\n if (routeMatch) {\n const templateName = routeMatch[1];\n if (templates.includes(templateName)) {\n const ext = await getTemplateExtension(templateName);\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${templateName} - HTMplar</title>\n <style>\n body { margin: 0; padding: 0; }\n #root { min-height: 100vh; }\n </style>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\">\n import React from 'react';\n import { createRoot } from 'react-dom/client';\n import EmailComponent from '/src/${templateName}.${ext}';\n\n const root = createRoot(document.getElementById('root'));\n root.render(React.createElement(EmailComponent));\n </script>\n </body>\n</html>`;\n\n res.setHeader('Content-Type', 'text/html');\n res.end(html);\n return;\n }\n }\n\n next();\n });\n },\n };\n}\n\nasync function getTemplateExtension(name: string): Promise<string> {\n const srcDir = path.join(process.cwd(), 'src');\n const files = await readdir(srcDir);\n const file = files.find((f) => f === `${name}.tsx` || f === `${name}.jsx`);\n return file?.endsWith('.tsx') ? 'tsx' : 'jsx';\n}\n\nexport async function startDevServer(options: DevOptions) {\n const port = parseInt(options.port, 10);\n\n try {\n // Disable telemetry\n process.env.VITE_TELEMETRY_DISABLED = '1';\n\n // Discover email templates\n const templates = await discoverEmailTemplates();\n\n // Create Vite dev server\n const server = await createServer({\n configFile: false,\n root: process.cwd(),\n server: {\n port,\n open: options.open,\n },\n plugins: [react(), createRoutingPlugin(templates)],\n resolve: {\n alias: {\n '@adidas/htmplar-core': path.resolve(process.cwd(), 'node_modules/@adidas/htmplar-core'),\n '@adidas/htmplar-renderer': path.resolve(\n process.cwd(),\n 'node_modules/@adidas/htmplar-renderer'\n ),\n },\n },\n });\n\n await server.listen();\n\n console.log('');\n console.log(chalk.green('✅ Dev server running!'));\n console.log('');\n console.log(chalk.cyan(` ➜ Local: http://localhost:${port}/`));\n console.log('');\n console.log(chalk.gray(' Press Ctrl+C to stop'));\n console.log('');\n\n // Keep process alive\n process.on('SIGTERM', async () => {\n await server.close();\n process.exit(0);\n });\n\n process.on('SIGINT', async () => {\n console.log('');\n console.log(chalk.yellow('Stopping dev server...'));\n await server.close();\n process.exit(0);\n });\n } catch (error) {\n console.error(chalk.red('Failed to start dev server:'), error);\n throw error;\n }\n}\n"]}
@@ -0,0 +1,78 @@
1
+ import express from 'express';
2
+ import { readdir } from 'fs/promises';
3
+ import { join } from 'path';
4
+ import chalk from 'chalk';
5
+
6
+ // src/commands/preview.ts
7
+ async function startPreviewServer(options) {
8
+ const port = parseInt(options.port, 10);
9
+ const cwd = process.cwd();
10
+ const distDir = join(cwd, "dist");
11
+ const app = express();
12
+ app.use(express.static(distDir));
13
+ app.get("/", async (_req, res) => {
14
+ try {
15
+ const files = await readdir(distDir);
16
+ const htmlFiles = files.filter((f) => f.endsWith(".html"));
17
+ const html = `
18
+ <!DOCTYPE html>
19
+ <html>
20
+ <head>
21
+ <meta charset="utf-8">
22
+ <title>HTMplar Preview</title>
23
+ <style>
24
+ body {
25
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
26
+ max-width: 800px;
27
+ margin: 50px auto;
28
+ padding: 20px;
29
+ }
30
+ h1 { color: #0066cc; }
31
+ ul { list-style: none; padding: 0; }
32
+ li { margin: 10px 0; }
33
+ a {
34
+ display: block;
35
+ padding: 15px;
36
+ background: #f4f4f4;
37
+ border-radius: 8px;
38
+ text-decoration: none;
39
+ color: #333;
40
+ transition: background 0.2s;
41
+ }
42
+ a:hover { background: #e0e0e0; }
43
+ </style>
44
+ </head>
45
+ <body>
46
+ <h1>\u{1F4E7} HTMplar Email Preview</h1>
47
+ <p>${htmlFiles.length} email(s) built</p>
48
+ <ul>
49
+ ${htmlFiles.map((file) => `<li><a href="/${file}">${file}</a></li>`).join("")}
50
+ </ul>
51
+ </body>
52
+ </html>
53
+ `;
54
+ res.send(html);
55
+ } catch {
56
+ res.status(500).send("Error reading dist directory");
57
+ }
58
+ });
59
+ app.listen(port, () => {
60
+ console.log("");
61
+ console.log(chalk.green("\u2705 Preview server running!"));
62
+ console.log("");
63
+ console.log(chalk.cyan(` \u279C Local: http://localhost:${port}/`));
64
+ console.log("");
65
+ console.log(chalk.gray(" Press Ctrl+C to stop"));
66
+ console.log("");
67
+ });
68
+ process.on("SIGTERM", () => process.exit(0));
69
+ process.on("SIGINT", () => {
70
+ console.log("");
71
+ console.log(chalk.yellow("Stopping preview server..."));
72
+ process.exit(0);
73
+ });
74
+ }
75
+
76
+ export { startPreviewServer };
77
+ //# sourceMappingURL=chunk-SEOAKQKB.mjs.map
78
+ //# sourceMappingURL=chunk-SEOAKQKB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/preview.ts"],"names":[],"mappings":";;;;;;AAUA,eAAsB,mBAAmB,OAAA,EAAyB;AAChE,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA;AACtC,EAAA,MAAM,GAAA,GAAM,QAAQ,GAAA,EAAI;AACxB,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,EAAK,MAAM,CAAA;AAEhC,EAAA,MAAM,MAAM,OAAA,EAAQ;AAGpB,EAAA,GAAA,CAAI,GAAA,CAAI,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAC,CAAA;AAG/B,EAAA,GAAA,CAAI,GAAA,CAAI,GAAA,EAAK,OAAO,IAAA,EAAM,GAAA,KAAQ;AAChC,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,OAAO,CAAA;AACnC,MAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,QAAA,CAAS,OAAO,CAAC,CAAA;AAEzD,MAAA,MAAM,IAAA,GAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAAA,EA8BZ,UAAU,MAAM,CAAA;AAAA;AAAA,IAAA,EAEjB,SAAA,CAAU,GAAA,CAAI,CAAC,IAAA,KAAS,CAAA,cAAA,EAAiB,IAAI,CAAA,EAAA,EAAK,IAAI,CAAA,SAAA,CAAW,CAAA,CAAE,IAAA,CAAK,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAM3E,MAAA,GAAA,CAAI,KAAK,IAAI,CAAA;AAAA,IACf,CAAA,CAAA,MAAQ;AACN,MAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,8BAA8B,CAAA;AAAA,IACrD;AAAA,EACF,CAAC,CAAA;AAED,EAAA,GAAA,CAAI,MAAA,CAAO,MAAM,MAAM;AACrB,IAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAA,CAAM,gCAA2B,CAAC,CAAA;AACpD,IAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,IAAA,OAAA,CAAQ,IAAI,KAAA,CAAM,IAAA,CAAK,CAAA,qCAAA,EAAmC,IAAI,GAAG,CAAC,CAAA;AAClE,IAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,IAAA,CAAK,yBAAyB,CAAC,CAAA;AACjD,IAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AAAA,EAChB,CAAC,CAAA;AAGD,EAAA,OAAA,CAAQ,GAAG,SAAA,EAAW,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAC,CAAA;AAC3C,EAAA,OAAA,CAAQ,EAAA,CAAG,UAAU,MAAM;AACzB,IAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,MAAA,CAAO,4BAA4B,CAAC,CAAA;AACtD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB,CAAC,CAAA;AACH","file":"chunk-SEOAKQKB.mjs","sourcesContent":["import express from 'express';\nimport { readdir } from 'fs/promises';\nimport { join } from 'path';\nimport chalk from 'chalk';\n\ninterface PreviewOptions {\n port: string;\n open: boolean;\n}\n\nexport async function startPreviewServer(options: PreviewOptions) {\n const port = parseInt(options.port, 10);\n const cwd = process.cwd();\n const distDir = join(cwd, 'dist');\n\n const app = express();\n\n // Serve static files from dist\n app.use(express.static(distDir));\n\n // Index page listing all emails\n app.get('/', async (_req, res) => {\n try {\n const files = await readdir(distDir);\n const htmlFiles = files.filter((f) => f.endsWith('.html'));\n\n const html = `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>HTMplar Preview</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n max-width: 800px;\n margin: 50px auto;\n padding: 20px;\n }\n h1 { color: #0066cc; }\n ul { list-style: none; padding: 0; }\n li { margin: 10px 0; }\n a {\n display: block;\n padding: 15px;\n background: #f4f4f4;\n border-radius: 8px;\n text-decoration: none;\n color: #333;\n transition: background 0.2s;\n }\n a:hover { background: #e0e0e0; }\n </style>\n</head>\n<body>\n <h1>📧 HTMplar Email Preview</h1>\n <p>${htmlFiles.length} email(s) built</p>\n <ul>\n ${htmlFiles.map((file) => `<li><a href=\"/${file}\">${file}</a></li>`).join('')}\n </ul>\n</body>\n</html>\n `;\n\n res.send(html);\n } catch {\n res.status(500).send('Error reading dist directory');\n }\n });\n\n app.listen(port, () => {\n console.log('');\n console.log(chalk.green('✅ Preview server running!'));\n console.log('');\n console.log(chalk.cyan(` ➜ Local: http://localhost:${port}/`));\n console.log('');\n console.log(chalk.gray(' Press Ctrl+C to stop'));\n console.log('');\n });\n\n // Handle shutdown\n process.on('SIGTERM', () => process.exit(0));\n process.on('SIGINT', () => {\n console.log('');\n console.log(chalk.yellow('Stopping preview server...'));\n process.exit(0);\n });\n}\n"]}
@@ -0,0 +1,52 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+
4
+ // src/cli.ts
5
+ var program = new Command();
6
+ program.name("htmplar").description("HTMplar - Modern email development with React").version("2.0.0-alpha.0");
7
+ program.command("dev").description("Start development server with hot reload").option("-p, --port <port>", "Port to run the server on", "3000").option("-o, --open", "Open browser automatically", false).action(async (options) => {
8
+ console.log(chalk.blue("\u{1F680} Starting HTMplar dev server..."));
9
+ console.log(chalk.gray(` Port: ${options.port}`));
10
+ try {
11
+ const { startDevServer } = await import('./dev-3URSSDZ2.mjs');
12
+ await startDevServer(options);
13
+ } catch (error) {
14
+ console.error(chalk.red("\u274C Failed to start dev server:"), error);
15
+ process.exit(1);
16
+ }
17
+ });
18
+ program.command("build").description("Build emails to static HTML").option("-o, --output <dir>", "Output directory", "dist").option("--clean", "Clean output directory before build", false).action(async (options) => {
19
+ console.log(chalk.blue("\u{1F4E6} Building emails..."));
20
+ try {
21
+ const { buildEmails } = await import('./build-P2H7OT3Y.mjs');
22
+ await buildEmails(options);
23
+ console.log(chalk.green("\u2705 Build complete!"));
24
+ } catch (error) {
25
+ console.error(chalk.red("\u274C Build failed:"), error);
26
+ process.exit(1);
27
+ }
28
+ });
29
+ program.command("preview").description("Preview built emails").option("-p, --port <port>", "Port to run preview server", "3001").option("-o, --open", "Open browser automatically", false).action(async (options) => {
30
+ console.log(chalk.blue("\u{1F440} Starting preview server..."));
31
+ try {
32
+ const { startPreviewServer } = await import('./preview-6ZXF4ZM7.mjs');
33
+ await startPreviewServer(options);
34
+ } catch (error) {
35
+ console.error(chalk.red("\u274C Failed to start preview:"), error);
36
+ process.exit(1);
37
+ }
38
+ });
39
+ program.command("init [name]").description("Initialize a new HTMplar project").action(async (name) => {
40
+ console.log(chalk.blue("\u{1F389} Initializing new HTMplar project..."));
41
+ try {
42
+ const { initProject } = await import('./init-52IMMAJT.mjs');
43
+ await initProject(name);
44
+ console.log(chalk.green("\u2705 Project initialized!"));
45
+ } catch (error) {
46
+ console.error(chalk.red("\u274C Initialization failed:"), error);
47
+ process.exit(1);
48
+ }
49
+ });
50
+ program.parse();
51
+ //# sourceMappingURL=chunk-VSA5JOPE.mjs.map
52
+ //# sourceMappingURL=chunk-VSA5JOPE.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"names":[],"mappings":"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","file":"chunk-VSA5JOPE.mjs","sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\n\nconst program = new Command();\n\nprogram\n .name('htmplar')\n .description('HTMplar - Modern email development with React')\n .version('2.0.0-alpha.0');\n\n// Dev command\nprogram\n .command('dev')\n .description('Start development server with hot reload')\n .option('-p, --port <port>', 'Port to run the server on', '3000')\n .option('-o, --open', 'Open browser automatically', false)\n .action(async (options) => {\n console.log(chalk.blue('🚀 Starting HTMplar dev server...'));\n console.log(chalk.gray(` Port: ${options.port}`));\n\n try {\n const { startDevServer } = await import('./commands/dev.js');\n await startDevServer(options);\n } catch (error) {\n console.error(chalk.red('❌ Failed to start dev server:'), error);\n process.exit(1);\n }\n });\n\n// Build command\nprogram\n .command('build')\n .description('Build emails to static HTML')\n .option('-o, --output <dir>', 'Output directory', 'dist')\n .option('--clean', 'Clean output directory before build', false)\n .action(async (options) => {\n console.log(chalk.blue('📦 Building emails...'));\n\n try {\n const { buildEmails } = await import('./commands/build.js');\n await buildEmails(options);\n console.log(chalk.green('✅ Build complete!'));\n } catch (error) {\n console.error(chalk.red('❌ Build failed:'), error);\n process.exit(1);\n }\n });\n\n// Preview command\nprogram\n .command('preview')\n .description('Preview built emails')\n .option('-p, --port <port>', 'Port to run preview server', '3001')\n .option('-o, --open', 'Open browser automatically', false)\n .action(async (options) => {\n console.log(chalk.blue('👀 Starting preview server...'));\n\n try {\n const { startPreviewServer } = await import('./commands/preview.js');\n await startPreviewServer(options);\n } catch (error) {\n console.error(chalk.red('❌ Failed to start preview:'), error);\n process.exit(1);\n }\n });\n\n// Init command\nprogram\n .command('init [name]')\n .description('Initialize a new HTMplar project')\n .action(async (name) => {\n console.log(chalk.blue('🎉 Initializing new HTMplar project...'));\n\n try {\n const { initProject } = await import('./commands/init.js');\n await initProject(name);\n console.log(chalk.green('✅ Project initialized!'));\n } catch (error) {\n console.error(chalk.red('❌ Initialization failed:'), error);\n process.exit(1);\n }\n });\n\nprogram.parse();\n"]}
package/dist/cli.mjs ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import './chunk-VSA5JOPE.mjs';
3
+ //# sourceMappingURL=cli.mjs.map
4
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"cli.mjs"}
@@ -0,0 +1,3 @@
1
+ export { startDevServer } from './chunk-PDVVYV5V.mjs';
2
+ //# sourceMappingURL=dev-3URSSDZ2.mjs.map
3
+ //# sourceMappingURL=dev-3URSSDZ2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"dev-3URSSDZ2.mjs"}
package/dist/index.mjs ADDED
@@ -0,0 +1,11 @@
1
+ export { startDevServer } from './chunk-PDVVYV5V.mjs';
2
+ export { buildEmails } from './chunk-3B2SEUWU.mjs';
3
+ export { startPreviewServer } from './chunk-SEOAKQKB.mjs';
4
+ export { initProject } from './chunk-AW7RBGLV.mjs';
5
+
6
+ // src/index.ts
7
+ var version = "2.0.0-alpha.0";
8
+
9
+ export { version };
10
+ //# sourceMappingURL=index.mjs.map
11
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAKO,IAAM,OAAA,GAAU","file":"index.mjs","sourcesContent":["/**\n * @adidas/htmplar-cli - Command line interface\n * @packageDocumentation\n */\n\nexport const version = '2.0.0-alpha.0';\n\n// Export commands\nexport { startDevServer } from './commands/dev';\nexport { buildEmails } from './commands/build';\nexport { startPreviewServer } from './commands/preview';\nexport { initProject } from './commands/init';\n"]}
@@ -0,0 +1,3 @@
1
+ export { initProject } from './chunk-AW7RBGLV.mjs';
2
+ //# sourceMappingURL=init-52IMMAJT.mjs.map
3
+ //# sourceMappingURL=init-52IMMAJT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"init-52IMMAJT.mjs"}
@@ -0,0 +1,3 @@
1
+ export { startPreviewServer } from './chunk-SEOAKQKB.mjs';
2
+ //# sourceMappingURL=preview-6ZXF4ZM7.mjs.map
3
+ //# sourceMappingURL=preview-6ZXF4ZM7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"preview-6ZXF4ZM7.mjs"}
@@ -0,0 +1,59 @@
1
+ import { renderToEmail, getBaseStyles } from '@adidas/htmplar-renderer';
2
+ import juice from 'juice';
3
+
4
+ // src/utils/renderEmail.ts
5
+ function inlineCss(html, options = {}) {
6
+ const {
7
+ removeStyleTags = false,
8
+ preserveMediaQueries = true,
9
+ preserveFontFaces = true
10
+ } = options;
11
+ try {
12
+ return juice(html, {
13
+ removeStyleTags,
14
+ preserveMediaQueries,
15
+ preserveFontFaces,
16
+ applyWidthAttributes: true,
17
+ applyHeightAttributes: true,
18
+ insertPreservedExtraCss: true
19
+ });
20
+ } catch (error) {
21
+ console.warn("CSS inlining failed, returning original HTML:", error);
22
+ return html;
23
+ }
24
+ }
25
+
26
+ // src/utils/renderEmail.ts
27
+ async function renderEmailFromFile(filePath, options = {}) {
28
+ const { includeBaseStyles = true, inlineCss: shouldInline = true } = options;
29
+ try {
30
+ const module = await import(filePath);
31
+ const Component = module.default || module;
32
+ if (typeof Component !== "function") {
33
+ throw new Error(`File ${filePath} does not export a valid React component`);
34
+ }
35
+ const element = Component({});
36
+ let html = renderToEmail(element, {
37
+ styles: includeBaseStyles ? getBaseStyles() : "",
38
+ includeDoctype: true
39
+ });
40
+ if (shouldInline) {
41
+ html = inlineCss(html, {
42
+ removeStyleTags: false,
43
+ // Keep style tags for media queries
44
+ preserveMediaQueries: true,
45
+ preserveFontFaces: true
46
+ });
47
+ }
48
+ return html;
49
+ } catch (error) {
50
+ if (error instanceof Error) {
51
+ throw new Error(`Failed to render ${filePath}: ${error.message}`);
52
+ }
53
+ throw error;
54
+ }
55
+ }
56
+
57
+ export { renderEmailFromFile };
58
+ //# sourceMappingURL=renderEmail-WCOTFX4J.mjs.map
59
+ //# sourceMappingURL=renderEmail-WCOTFX4J.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/inlineCss.ts","../src/utils/renderEmail.ts"],"names":[],"mappings":";;;;AAYO,SAAS,SAAA,CAAU,IAAA,EAAc,OAAA,GAA4B,EAAC,EAAW;AAC9E,EAAA,MAAM;AAAA,IACJ,eAAA,GAAkB,KAAA;AAAA,IAClB,oBAAA,GAAuB,IAAA;AAAA,IACvB,iBAAA,GAAoB;AAAA,GACtB,GAAI,OAAA;AAEJ,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,IAAA,EAAM;AAAA,MACjB,eAAA;AAAA,MACA,oBAAA;AAAA,MACA,iBAAA;AAAA,MACA,oBAAA,EAAsB,IAAA;AAAA,MACtB,qBAAA,EAAuB,IAAA;AAAA,MACvB,uBAAA,EAAyB;AAAA,KAC1B,CAAA;AAAA,EACH,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,IAAA,CAAK,iDAAiD,KAAK,CAAA;AACnE,IAAA,OAAO,IAAA;AAAA,EACT;AACF;;;ACtBA,eAAsB,mBAAA,CACpB,QAAA,EACA,OAAA,GAA8B,EAAC,EACd;AACjB,EAAA,MAAM,EAAE,iBAAA,GAAoB,IAAA,EAAM,SAAA,EAAW,YAAA,GAAe,MAAK,GAAI,OAAA;AAErE,EAAA,IAAI;AAEF,IAAA,MAAM,MAAA,GAAS,MAAM,OAAO,QAAA,CAAA;AAC5B,IAAA,MAAM,SAAA,GAAY,OAAO,OAAA,IAAW,MAAA;AAEpC,IAAA,IAAI,OAAO,cAAc,UAAA,EAAY;AACnC,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,KAAA,EAAQ,QAAQ,CAAA,wCAAA,CAA0C,CAAA;AAAA,IAC5E;AAGA,IAAA,MAAM,OAAA,GAAU,SAAA,CAAU,EAAE,CAAA;AAG5B,IAAA,IAAI,IAAA,GAAO,cAAc,OAAA,EAAS;AAAA,MAChC,MAAA,EAAQ,iBAAA,GAAoB,aAAA,EAAc,GAAI,EAAA;AAAA,MAC9C,cAAA,EAAgB;AAAA,KACjB,CAAA;AAGD,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,IAAA,GAAO,UAAU,IAAA,EAAM;AAAA,QACrB,eAAA,EAAiB,KAAA;AAAA;AAAA,QACjB,oBAAA,EAAsB,IAAA;AAAA,QACtB,iBAAA,EAAmB;AAAA,OACpB,CAAA;AAAA,IACH;AAEA,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,iBAAA,EAAoB,QAAQ,CAAA,EAAA,EAAK,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,IAClE;AACA,IAAA,MAAM,KAAA;AAAA,EACR;AACF","file":"renderEmail-WCOTFX4J.mjs","sourcesContent":["import juice from 'juice';\n\nexport interface InlineCssOptions {\n removeStyleTags?: boolean;\n preserveMediaQueries?: boolean;\n preserveFontFaces?: boolean;\n}\n\n/**\n * Inline CSS styles in HTML for email compatibility\n * Uses juice library to convert <style> tags to inline styles\n */\nexport function inlineCss(html: string, options: InlineCssOptions = {}): string {\n const {\n removeStyleTags = false,\n preserveMediaQueries = true,\n preserveFontFaces = true,\n } = options;\n\n try {\n return juice(html, {\n removeStyleTags,\n preserveMediaQueries,\n preserveFontFaces,\n applyWidthAttributes: true,\n applyHeightAttributes: true,\n insertPreservedExtraCss: true,\n });\n } catch (error) {\n console.warn('CSS inlining failed, returning original HTML:', error);\n return html;\n }\n}\n","import { renderToEmail, getBaseStyles } from '@adidas/htmplar-renderer';\nimport type { ReactElement } from 'react';\nimport { inlineCss } from './inlineCss.js';\n\ninterface RenderEmailOptions {\n includeBaseStyles?: boolean;\n inlineCss?: boolean;\n minify?: boolean;\n}\n\nexport async function renderEmailFromFile(\n filePath: string,\n options: RenderEmailOptions = {}\n): Promise<string> {\n const { includeBaseStyles = true, inlineCss: shouldInline = true } = options;\n\n try {\n // Dynamically import the email component\n const module = await import(filePath);\n const Component = module.default || module;\n\n if (typeof Component !== 'function') {\n throw new Error(`File ${filePath} does not export a valid React component`);\n }\n\n // Create element\n const element = Component({}) as ReactElement;\n\n // Render to email HTML\n let html = renderToEmail(element, {\n styles: includeBaseStyles ? getBaseStyles() : '',\n includeDoctype: true,\n });\n\n // Inline CSS for email compatibility\n if (shouldInline) {\n html = inlineCss(html, {\n removeStyleTags: false, // Keep style tags for media queries\n preserveMediaQueries: true,\n preserveFontFaces: true,\n });\n }\n\n return html;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to render ${filePath}: ${error.message}`);\n }\n throw error;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,4 +1,54 @@
1
1
  {
2
2
  "name": "@adidas/htmplar-cli",
3
- "version": "0.0.0"
3
+ "version": "2.0.0-alpha.1",
4
+ "description": "Command line interface for HTMplar",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/adidas/htmplar.git",
9
+ "directory": "packages/cli"
10
+ },
11
+ "homepage": "https://github.com/adidas/htmplar#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/adidas/htmplar/issues"
14
+ },
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "bin": {
18
+ "htmplar": "./dist/bin.mjs"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "dev": "tsup --watch",
26
+ "lint": "eslint src",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "typecheck": "tsc --noEmit"
30
+ },
31
+ "dependencies": {
32
+ "@adidas/htmplar-core": "*",
33
+ "@adidas/htmplar-renderer": "*",
34
+ "@vitejs/plugin-react": "^4.3.2",
35
+ "chalk": "^5.3.0",
36
+ "chokidar": "^3.6.0",
37
+ "commander": "^12.1.0",
38
+ "cosmiconfig": "^9.0.0",
39
+ "express": "^4.21.0",
40
+ "inquirer": "^10.2.2",
41
+ "juice": "^10.0.1",
42
+ "ora": "^8.1.0",
43
+ "vite": "^8.3.0",
44
+ "zod": "^3.23.8",
45
+ "react": "^18.3.1"
46
+ },
47
+ "devDependencies": {
48
+ "@types/express": "^4.17.21",
49
+ "@types/inquirer": "^9.0.7"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ }
4
54
  }