@emberkit/cli 0.6.7 → 0.6.8

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/brand.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * ANSI terminal brand marks (no emoji) for consistent CLI branding.
3
+ */
4
+ export const cliBrand = {
5
+ orange: "\x1b[38;5;208m",
6
+ cyan: "\x1b[38;5;51m",
7
+ red: "\x1b[38;5;196m",
8
+ reset: "\x1b[0m",
9
+ /** EmberKit logo mark in terminal output */
10
+ logo() {
11
+ return `${cliBrand.orange}◆${cliBrand.reset}`;
12
+ },
13
+ /** Success / highlight accent */
14
+ spark() {
15
+ return `${cliBrand.cyan}✦${cliBrand.reset}`;
16
+ },
17
+ /** Error line prefix */
18
+ fail() {
19
+ return `${cliBrand.red}◆${cliBrand.reset}`;
20
+ },
21
+ };
package/dist/cli.js CHANGED
@@ -7,6 +7,7 @@ import { serve } from "./commands/serve.js";
7
7
  import { create, TEMPLATES } from "./commands/create.js";
8
8
  import { generate } from "./utils/generator.js";
9
9
  import { toKebabCase } from "./templates/index.js";
10
+ import { cliBrand } from "./brand.js";
10
11
  export async function runCLI(args) {
11
12
  const [command, ...restArgs] = args.slice(2);
12
13
  if (!command) {
@@ -49,7 +50,7 @@ export async function runCLI(args) {
49
50
  }
50
51
  function showHelp() {
51
52
  console.log(`
52
- 🔥 EmberKit CLI v${getCliPackageVersion()}
53
+ ${cliBrand.logo()} EmberKit CLI v${getCliPackageVersion()}
53
54
 
54
55
  Usage: emberkit <command> [options]
55
56
 
@@ -134,7 +135,7 @@ async function handleCreate(args) {
134
135
  }
135
136
  function showCreateHelp() {
136
137
  console.log(`
137
- 🔥 EmberKit — Create a new project
138
+ ${cliBrand.logo()} EmberKit — Create a new project
138
139
 
139
140
  Usage: emberkit create [name] [options]
140
141
 
@@ -206,10 +207,10 @@ async function runGenerate(args) {
206
207
  `${typeConfig.dir}/${toKebabCase(name)}${typeConfig.ext}`;
207
208
  const result = await generate({ name, path: filePath, template: type });
208
209
  if (result.success) {
209
- console.log(`✓ Generated ${type}: ${result.path}`);
210
+ console.log(`${cliBrand.spark()} Generated ${type}: ${result.path}`);
210
211
  }
211
212
  else {
212
- console.error(`✗ ${result.error}`);
213
+ console.error(`${cliBrand.fail()} ${result.error}`);
213
214
  process.exit(1);
214
215
  }
215
216
  }
@@ -2,6 +2,8 @@ import { build as viteBuild } from "vite";
2
2
  import { join } from "path";
3
3
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
4
  import { pathToFileURL } from "url";
5
+ import { cliBrand } from "../brand.js";
6
+ import { mergeEmberkitViteConfig } from "../utils/merge-emberkit-vite.js";
5
7
  const COLORS = {
6
8
  reset: "\x1b[0m",
7
9
  bright: "\x1b[1m",
@@ -71,9 +73,10 @@ async function loadViteConfig(root) {
71
73
  }
72
74
  export async function build(_args) {
73
75
  const root = process.cwd();
74
- console.log(`\n${COLORS.orange}🔥 EmberKit Build${COLORS.reset}\n`);
76
+ console.log(`\n${cliBrand.logo()} ${COLORS.orange}EmberKit Build${COLORS.reset}\n`);
75
77
  const emberkitConfig = await loadEmberKitConfig(root);
76
- const viteConfig = await loadViteConfig(root);
78
+ const viteFileConfig = await loadViteConfig(root);
79
+ const viteConfig = mergeEmberkitViteConfig(emberkitConfig, viteFileConfig);
77
80
  const mode = emberkitConfig?.mode || "hybrid";
78
81
  const outDir = emberkitConfig?.build?.outDir || "dist";
79
82
  log("info", `Build mode: ${COLORS.ember}${mode}${COLORS.reset}`);
@@ -117,7 +120,7 @@ export async function build(_args) {
117
120
  await prerenderStaticRoutes(root, outDir, true);
118
121
  log("success", "Static build complete!");
119
122
  }
120
- console.log(`\n${COLORS.green}Build finished successfully!${COLORS.reset}`);
123
+ console.log(`\n${cliBrand.spark()} ${COLORS.green}Build finished successfully!${COLORS.reset}\n`);
121
124
  console.log(`${COLORS.gray} Output: ${COLORS.cyan}./${outDir}${COLORS.reset}\n`);
122
125
  }
123
126
  catch (error) {
@@ -165,7 +168,7 @@ const routeToRegex = (routePath) => {
165
168
  paramNames.push(name);
166
169
  return '([^/]+)';
167
170
  });
168
- return { regex: new RegExp('^' + regexStr + '$'), paramNames };
171
+ return { regex: new RegExp('^' + regexStr.replace(/\\/$/, '/?') + '$'), paramNames };
169
172
  };
170
173
 
171
174
  const matchRoute = (routeList, pathname) => {
@@ -486,10 +489,10 @@ async function prerenderStaticRoutes(root, outDir, prerenderAll = false) {
486
489
  mkdirSync(outputDir, { recursive: true });
487
490
  }
488
491
  writeFileSync(outputPath, html, "utf-8");
489
- console.log(` ${COLORS.green}✓${COLORS.reset} ${route.path}`);
492
+ console.log(` ${cliBrand.spark()} ${COLORS.green}${route.path}${COLORS.reset}`);
490
493
  }
491
494
  catch (e) {
492
- console.log(` ${COLORS.red}✗${COLORS.reset} ${route.path} - ${e}`);
495
+ console.log(` ${COLORS.red}◆${COLORS.reset} ${COLORS.red}${route.path} - ${e}${COLORS.reset}`);
493
496
  }
494
497
  }
495
498
  }
@@ -10,6 +10,7 @@ import { blogTemplate } from "../templates/project-templates/blog/blog.js";
10
10
  import { saasTemplate } from "../templates/project-templates/saas/saas.js";
11
11
  import { dashboardTemplate } from "../templates/project-templates/dashboard/dashboard.js";
12
12
  import { apiTemplate } from "../templates/project-templates/api/api.js";
13
+ import { cliBrand } from "../brand.js";
13
14
  const RESET = "\x1b[0m";
14
15
  const BOLD = "\x1b[1m";
15
16
  const DIM = "\x1b[2m";
@@ -45,7 +46,7 @@ function printTemplateList() {
45
46
  function printHeader() {
46
47
  const header = `
47
48
  ${BRIGHT_BLACK}╭─────────────────────────────────────────────────────╮${RESET}
48
- ${BRIGHT_BLACK}│${RESET} ${ORANGE_BG}${BRIGHT_BLACK} EmberKit ${RESET} ${BRIGHT_BLACK}│${RESET}
49
+ ${BRIGHT_BLACK}│${RESET} ${cliBrand.logo()} ${ORANGE_BG}${BRIGHT_BLACK} EmberKit ${RESET} ${BRIGHT_BLACK}│${RESET}
49
50
  ${BRIGHT_BLACK}│${RESET} ${DIM}A minimalist TypeScript-first JSX framework${RESET} ${BRIGHT_BLACK}│${RESET}
50
51
  ${BRIGHT_BLACK}╰─────────────────────────────────────────────────────╯${RESET}
51
52
  `;
@@ -58,11 +59,11 @@ function printStep(step, total, message) {
58
59
  console.log(` ${numStr} ${BRIGHT_WHITE + message + RESET} ${bar}`);
59
60
  }
60
61
  function printSuccess(message) {
61
- const check = BRIGHT_GREEN + "✓" + RESET;
62
+ const check = cliBrand.spark();
62
63
  console.log(`\n ${check} ${BRIGHT_GREEN + message + RESET}\n`);
63
64
  }
64
65
  function printError(message) {
65
- const err = BRIGHT_RED + "✗" + RESET;
66
+ const err = `${BRIGHT_RED}◆${RESET}`;
66
67
  console.log(`\n ${err} ${BRIGHT_RED + message + RESET}\n`);
67
68
  }
68
69
  function printInfo(message) {
@@ -2,15 +2,7 @@ import { createServer } from "vite";
2
2
  import { join } from "path";
3
3
  import { existsSync } from "fs";
4
4
  import { pathToFileURL } from "url";
5
- const EMBERKIT_ASCII = `
6
- ╔═══════════════════════════════════════╗
7
- ║ ║
8
- ║ 🔥 E M B E R K I T 🔥 ║
9
- ║ ║
10
- ║ ░▒▓█ DEV SERVER █▓▒░ ║
11
- ║ ║
12
- ╚═══════════════════════════════════════╝
13
- `;
5
+ import { mergeEmberkitViteConfig } from "../utils/merge-emberkit-vite.js";
14
6
  const COLORS = {
15
7
  reset: "\x1b[0m",
16
8
  bright: "\x1b[1m",
@@ -23,6 +15,15 @@ const COLORS = {
23
15
  yellow: "\x1b[38;5;220m",
24
16
  red: "\x1b[38;5;196m",
25
17
  };
18
+ const EMBERKIT_ASCII = `
19
+ ╔═══════════════════════════════════════╗
20
+ ║ ║
21
+ ║ ${COLORS.orange}◆${COLORS.reset} E M B E R K I T ${COLORS.orange}◆${COLORS.reset} ║
22
+ ║ ║
23
+ ║ ░▒▓█ DEV SERVER █▓▒░ ║
24
+ ║ ║
25
+ ╚═══════════════════════════════════════╝
26
+ `;
26
27
  function log(level, message, meta) {
27
28
  const timestamp = new Date().toLocaleTimeString("en-US", { hour12: false });
28
29
  const prefix = `${COLORS.gray}[${timestamp}]${COLORS.reset}`;
@@ -83,7 +84,8 @@ export async function dev(_args) {
83
84
  console.log(`${COLORS.orange}${EMBERKIT_ASCII}${COLORS.reset}`);
84
85
  log("info", "Initializing development server...");
85
86
  const emberkitConfig = await loadEmberKitConfig(root);
86
- const viteConfig = await loadViteConfig(root);
87
+ const viteFileConfig = await loadViteConfig(root);
88
+ const viteConfig = mergeEmberkitViteConfig(emberkitConfig, viteFileConfig);
87
89
  if (emberkitConfig) {
88
90
  log("debug", "Loaded emberkit.config", { mode: emberkitConfig.mode || "hybrid" });
89
91
  }
@@ -17,7 +17,7 @@ const COLORS = {
17
17
  const EMBERKIT_ASCII = `
18
18
  ╔═══════════════════════════════════════╗
19
19
  ║ ║
20
- 🔥 E M B E R K I T 🔥
20
+ ${COLORS.orange}◆${COLORS.reset} E M B E R K I T ${COLORS.orange}◆${COLORS.reset}
21
21
  ║ ║
22
22
  ║ ░▒▓█ PREVIEW SERVER █▓▒░ ║
23
23
  ║ ║
@@ -17,7 +17,7 @@ const COLORS = {
17
17
  const EMBERKIT_ASCII = `
18
18
  ╔═══════════════════════════════════════╗
19
19
  ║ ║
20
- 🔥 E M B E R K I T 🔥
20
+ ${COLORS.orange}◆${COLORS.reset} E M B E R K I T ${COLORS.orange}◆${COLORS.reset}
21
21
  ║ ║
22
22
  ║ ░▒▓█ PRODUCTION SERVER █▓▒░ ║
23
23
  ║ ║
@@ -1,10 +1,10 @@
1
1
  // Semver ranges for @emberkit/* packages written into generated projects.
2
2
  // When releasing libraries, bump these to match packages/*/package.json "version".
3
3
  export const EMBERKIT_PACKAGE_VERSIONS = {
4
- core: "^0.3.5",
5
- ui: "^1.0.0",
6
- icons: "^1.0.5",
7
- cli: "^0.6.7",
4
+ core: "^0.3.8",
5
+ ui: "^1.0.1",
6
+ icons: "^1.0.8",
7
+ cli: "^0.6.8",
8
8
  edge: "^0.2.4",
9
9
  tsconfig: "^0.2.1",
10
10
  };
@@ -119,9 +119,9 @@ const HomePage: RouteComponent = () => {
119
119
  const [activeTab, setActiveTab] = createSignal('features');
120
120
 
121
121
  const features = [
122
- { icon: '', title: 'Lightning Fast', desc: 'Sub-10KB runtime with tree-shakeable architecture' },
123
- { icon: '📘', title: 'TypeScript First', desc: 'Full type safety with intelligent autocomplete' },
124
- { icon: '📁', title: 'File-Based Routing', desc: 'Routes automatically created from your file structure' },
122
+ { icon: 'zap' as const, title: 'Lightning Fast', desc: 'Sub-10KB runtime with tree-shakeable architecture' },
123
+ { icon: 'book' as const, title: 'TypeScript First', desc: 'Full type safety with intelligent autocomplete' },
124
+ { icon: 'folder' as const, title: 'File-Based Routing', desc: 'Routes automatically created from your file structure' },
125
125
  ];
126
126
 
127
127
  const components = [
@@ -139,8 +139,9 @@ const HomePage: RouteComponent = () => {
139
139
  <section className="relative text-center py-20">
140
140
  <div className="pointer-events-none absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 h-[400px] w-[400px] rounded-full bg-ember-500/15 blur-[150px] animate-pulse-glow" />
141
141
  <div className="relative z-10 space-y-6 animate-fade-in-down">
142
- <Badge variant="primary" className="inline-block">
143
- Welcome to {{name}}
142
+ <Badge variant="primary" className="inline-flex items-center gap-2">
143
+ <Icon name="emberkit" size={14} className="text-amber-400 shrink-0 drop-shadow-[0_0_6px_rgba(251,191,36,0.45)]" />
144
+ Welcome to {{name}}
144
145
  </Badge>
145
146
  <Heading level="h1" size="4xl" weight="bold">
146
147
  Built with EmberKit <span className="bg-gradient-to-r from-ember-400 via-ember-500 to-amber-500 bg-clip-text text-transparent">UI System</span>
@@ -172,7 +173,9 @@ const HomePage: RouteComponent = () => {
172
173
  <Card key={feature.title} padding="lg" className="relative group hover:border-ember-500/50 transition-all hover:-translate-y-1 cursor-pointer">
173
174
  <div className="absolute inset-0 rounded-xl bg-gradient-to-br from-ember-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
174
175
  <div className="relative space-y-3">
175
- <div className="text-3xl">{feature.icon}</div>
176
+ <div className="flex h-14 w-14 items-center justify-center rounded-xl bg-gradient-to-br from-ember-400/20 via-fuchsia-500/10 to-cyan-500/10 text-ember-300 ring-1 ring-ember-400/25 shadow-[0_0_24px_rgba(236,72,153,0.12)]">
177
+ <Icon name={feature.icon} size={28} className="drop-shadow-[0_0_10px_rgba(249,115,22,0.35)]" />
178
+ </div>
176
179
  <Heading level="h3" size="md" weight="semibold">
177
180
  {feature.title}
178
181
  </Heading>
@@ -230,10 +233,16 @@ const HomePage: RouteComponent = () => {
230
233
  </div>
231
234
  </div>
232
235
  <Alert variant="success">
233
- This is a success alert message. Use it to confirm important actions.
236
+ <span className="inline-flex items-start gap-2">
237
+ <Icon name="check" size={18} className="text-emerald-400 shrink-0 mt-0.5" />
238
+ <span>This is a success alert message. Use it to confirm important actions.</span>
239
+ </span>
234
240
  </Alert>
235
241
  <Alert variant="info">
236
- This is an info alert. Useful for displaying helpful information.
242
+ <span className="inline-flex items-start gap-2">
243
+ <Icon name="info" size={18} className="text-sky-400 shrink-0 mt-0.5" />
244
+ <span>This is an info alert. Useful for displaying helpful information.</span>
245
+ </span>
237
246
  </Alert>
238
247
  </div>
239
248
  </Card>
@@ -284,14 +293,14 @@ const HomePage: RouteComponent = () => {
284
293
  export default HomePage;`,
285
294
  "src/routes/about.tsx": `import type { RouteComponent } from '@emberkit/core';
286
295
  import { Head } from '@emberkit/core';
287
- import { Heading, Text, Button, Card, Badge, Alert } from '@emberkit/ui';
296
+ import { Heading, Text, Button, Card, Badge, Alert, Icon } from '@emberkit/ui';
288
297
 
289
298
  const AboutPage: RouteComponent = () => {
290
299
  const features = [
291
- { icon: '⚙️', title: 'TypeScript-first', desc: 'Full type safety with intelligent autocomplete' },
292
- { icon: '🎨', title: 'UI Components', desc: 'Pre-built design system components' },
293
- { icon: '🎯', title: 'Tailwind CSS', desc: 'Utility-first styling framework' },
294
- { icon: '📁', title: 'File Routing', desc: 'Automatic routes from file structure' },
300
+ { icon: 'type' as const, title: 'TypeScript-first', desc: 'Full type safety with intelligent autocomplete' },
301
+ { icon: 'grid' as const, title: 'UI Components', desc: 'Pre-built design system components' },
302
+ { icon: 'zap' as const, title: 'Tailwind CSS', desc: 'Utility-first styling framework' },
303
+ { icon: 'folder' as const, title: 'File Routing', desc: 'Automatic routes from file structure' },
295
304
  ];
296
305
 
297
306
  const techStack = ['EmberKit', 'TypeScript', 'Tailwind CSS', 'Vite', 'JSX', 'Design System'];
@@ -331,7 +340,9 @@ const AboutPage: RouteComponent = () => {
331
340
  <div className="grid sm:grid-cols-2 gap-4">
332
341
  {features.map((f) => (
333
342
  <Card key={f.title} padding="lg" className="hover:border-ember-500/50 transition-all hover:-translate-y-0.5">
334
- <div className="text-2xl mb-3">{f.icon}</div>
343
+ <div className="flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br from-ember-400/20 via-fuchsia-500/10 to-cyan-500/10 text-ember-300 ring-1 ring-ember-400/25 mb-3">
344
+ <Icon name={f.icon} size={24} className="drop-shadow-[0_0_8px_rgba(249,115,22,0.35)]" />
345
+ </div>
335
346
  <Heading level="h3" size="md" weight="semibold" className="mb-1">
336
347
  {f.title}
337
348
  </Heading>
@@ -359,7 +370,10 @@ const AboutPage: RouteComponent = () => {
359
370
 
360
371
  {/* Benefits Alert */}
361
372
  <Alert variant="success">
362
- <strong>Pro Tip:</strong> This template uses the EmberKit design system components. Check the component library documentation to learn about all available components and their capabilities.
373
+ <span className="inline-flex items-start gap-2">
374
+ <Icon name="emberkit" size={18} className="text-amber-400 shrink-0 mt-0.5 drop-shadow-[0_0_8px_rgba(251,191,36,0.4)]" />
375
+ <span><strong>Pro Tip:</strong> This template uses the EmberKit design system components. Check the component library documentation to learn about all available components and their capabilities.</span>
376
+ </span>
363
377
  </Alert>
364
378
 
365
379
  {/* Back Button */}
@@ -0,0 +1,35 @@
1
+ function pluginsToArray(plugins) {
2
+ if (plugins == null)
3
+ return [];
4
+ return Array.isArray(plugins) ? plugins : [plugins];
5
+ }
6
+ /**
7
+ * Merges `emberkit.config` `vite` block with optional `vite.config.*` (file wins on top-level keys;
8
+ * `plugins` from both are concatenated in order).
9
+ */
10
+ export function mergeEmberkitViteConfig(emberkitConfig, viteFileConfig) {
11
+ const fromEmber = emberkitConfig?.vite ?? {};
12
+ const fromFile = viteFileConfig ?? {};
13
+ return {
14
+ ...fromEmber,
15
+ ...fromFile,
16
+ plugins: [...pluginsToArray(fromEmber.plugins), ...pluginsToArray(fromFile.plugins)],
17
+ server: { ...fromEmber.server, ...fromFile.server },
18
+ define: {
19
+ ...fromEmber.define,
20
+ ...fromFile.define,
21
+ },
22
+ css: { ...fromEmber.css, ...fromFile.css },
23
+ optimizeDeps: { ...fromEmber.optimizeDeps, ...fromFile.optimizeDeps },
24
+ resolve: { ...fromEmber.resolve, ...fromFile.resolve },
25
+ esbuild: { ...fromEmber.esbuild, ...fromFile.esbuild },
26
+ build: {
27
+ ...fromEmber.build,
28
+ ...fromFile.build,
29
+ rollupOptions: {
30
+ ...(fromEmber.build?.rollupOptions ?? {}),
31
+ ...(fromFile.build?.rollupOptions ?? {}),
32
+ },
33
+ },
34
+ };
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emberkit/cli",
3
- "version": "0.6.7",
3
+ "version": "0.6.8",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "CLI tool for EmberKit projects",