@emberkit/cli 0.3.1 → 0.4.0

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.
@@ -65,9 +65,7 @@ const starterFiles = {
65
65
  "scripts": {
66
66
  "dev": "emberkit dev",
67
67
  "build": "emberkit build",
68
- "preview": "emberkit preview",
69
- "lint": "eslint src --ext .ts,.tsx",
70
- "format": "prettier --write \\"src/**/*.{ts,tsx}\\""
68
+ "preview": "emberkit preview"
71
69
  },
72
70
  "dependencies": {
73
71
  "@emberkit/core": "^0.2.4"
@@ -75,7 +73,9 @@ const starterFiles = {
75
73
  "devDependencies": {
76
74
  "@emberkit/cli": "^0.2.4",
77
75
  "typescript": "^5.7.0",
78
- "vite": "^6.0.0"
76
+ "vite": "^6.0.0",
77
+ "tailwindcss": "^4.0.0",
78
+ "@tailwindcss/vite": "^4.0.0"
79
79
  }
80
80
  }`,
81
81
  "tsconfig.json": `{
@@ -100,27 +100,16 @@ const starterFiles = {
100
100
  "include": ["src"],
101
101
  "exclude": ["node_modules", "dist"]
102
102
  }`,
103
- "emberkit.config.ts": `import { defineConfig } from '@emberkit/core';
104
-
105
- export default defineConfig({
106
- mode: 'spa',
107
- build: {
108
- outDir: 'dist',
109
- target: 'esnext',
110
- },
111
- });`,
112
103
  "vite.config.ts": `import { defineConfig } from 'vite';
113
104
  import { emberkitVitePlugin } from '@emberkit/core/vite-plugin';
105
+ import tailwindcss from '@tailwindcss/vite';
114
106
 
115
107
  export default defineConfig({
116
- plugins: [emberkitVitePlugin()],
108
+ plugins: [emberkitVitePlugin(), tailwindcss()],
117
109
  server: {
118
110
  port: 3000,
119
111
  host: 'localhost',
120
112
  },
121
- esbuild: {
122
- jsxImportSource: '@emberkit/core',
123
- },
124
113
  });`,
125
114
  "index.html": `<!DOCTYPE html>
126
115
  <html lang="en">
@@ -131,11 +120,6 @@ export default defineConfig({
131
120
  <link rel="preconnect" href="https://fonts.googleapis.com">
132
121
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
133
122
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
134
- <style>
135
- * { box-sizing: border-box; margin: 0; padding: 0; }
136
- body { font-family: 'Inter', sans-serif; background: #0f172a; color: #e2e8f0; }
137
- a { color: inherit; text-decoration: none; }
138
- </style>
139
123
  </head>
140
124
  <body>
141
125
  <div id="app"></div>
@@ -149,6 +133,29 @@ const root = document.getElementById('app');
149
133
 
150
134
  if (root) {
151
135
  render(App, root);
136
+ }`,
137
+ "src/styles.css": `@import "tailwindcss";
138
+
139
+ @theme {
140
+ --color-ember-50: #fff7ed;
141
+ --color-ember-100: #ffedd5;
142
+ --color-ember-200: #fed7aa;
143
+ --color-ember-300: #fdba74;
144
+ --color-ember-400: #fb923c;
145
+ --color-ember-500: #f97316;
146
+ --color-ember-600: #ea580c;
147
+ --color-ember-700: #c2410c;
148
+ --color-ember-800: #9a3412;
149
+ --color-ember-900: #7c2d12;
150
+ --font-sans: 'Inter', system-ui, sans-serif;
151
+ }
152
+
153
+ body {
154
+ @apply bg-slate-900 text-slate-200 font-sans min-h-screen;
155
+ }
156
+
157
+ a {
158
+ @apply text-inherit no-underline transition-colors;
152
159
  }`,
153
160
  "src/routes/_layout.tsx": `import type { RouteComponent } from '@emberkit/core';
154
161
  import { Head } from '@emberkit/core';
@@ -160,21 +167,27 @@ const Layout: RouteComponent = ({ children }) => {
160
167
  <title>{{name}}</title>
161
168
  <meta name="description" content="Built with EmberKit" />
162
169
  </Head>
163
- <div className="app">
164
- <header className="header">
165
- <div className="logo">
166
- <span className="logo-icon">🔥</span>
167
- <span className="logo-text">{{name}}</span>
170
+ <div className="flex flex-col min-h-screen">
171
+ <header className="border-b border-slate-800 bg-slate-900/50 backdrop-blur sticky top-0 z-50">
172
+ <div className="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
173
+ <a href="/" className="flex items-center gap-2 group">
174
+ <span className="text-2xl">🔥</span>
175
+ <span className="text-xl font-bold bg-gradient-to-r from-ember-400 to-ember-500 bg-clip-text text-transparent">
176
+ {{name}}
177
+ </span>
178
+ </a>
179
+ <nav className="flex items-center gap-6">
180
+ <a href="/" className="text-slate-400 hover:text-ember-500 font-medium">Home</a>
181
+ <a href="/about" className="text-slate-400 hover:text-ember-500 font-medium">About</a>
182
+ <a href="https://emberkit.dev/docs" target="_blank" className="text-slate-400 hover:text-ember-500 font-medium">
183
+ Docs <span className="text-xs">↗</span>
184
+ </a>
185
+ </nav>
168
186
  </div>
169
- <nav className="nav">
170
- <a href="/" className="nav-link">Home</a>
171
- <a href="/about" className="nav-link">About</a>
172
- <a href="https://emberkit.dev/docs" className="nav-link" target="_blank">Docs →</a>
173
- </nav>
174
187
  </header>
175
- <main className="main">{children}</main>
176
- <footer className="footer">
177
- <p>Built with <a href="https://emberkit.dev" className="footer-link">EmberKit</a></p>
188
+ <main className="flex-1">{children}</main>
189
+ <footer className="border-t border-slate-800 py-8 text-center text-slate-500">
190
+ <p>Built with <a href="https://emberkit.dev" className="text-ember-500 hover:underline">EmberKit</a></p>
178
191
  </footer>
179
192
  </div>
180
193
  </>
@@ -189,60 +202,68 @@ const HomePage: RouteComponent = () => {
189
202
  const count = signal(0);
190
203
 
191
204
  return (
192
- <div className="home">
193
- <section className="hero">
194
- <h1 className="hero-title">
195
- Welcome to <span className="gradient-text">{{name}}</span>
205
+ <div className="max-w-6xl mx-auto px-6 py-16 space-y-20">
206
+ <section className="text-center space-y-6">
207
+ <h1 className="text-5xl md:text-6xl font-extrabold tracking-tight">
208
+ Welcome to <span className="bg-gradient-to-r from-ember-400 via-ember-500 to-ember-600 bg-clip-text text-transparent">{{name}}</span>
196
209
  </h1>
197
- <p className="hero-desc">
210
+ <p className="text-xl text-slate-400 max-w-2xl mx-auto">
198
211
  A minimalist TypeScript-first JSX framework built for speed and simplicity.
199
212
  Get started in seconds with hot module replacement and zero-config routing.
200
213
  </p>
201
- <div className="hero-actions">
202
- <a href="/about" className="btn btn-primary">
214
+ <div className="flex gap-4 justify-center pt-4">
215
+ <a href="/about" className="px-6 py-3 bg-ember-500 hover:bg-ember-600 text-white font-semibold rounded-lg transition-all hover:scale-105">
203
216
  Learn More
204
217
  </a>
205
- <a href="https://emberkit.dev/docs" target="_blank" className="btn btn-secondary">
218
+ <a href="https://emberkit.dev/docs" target="_blank" className="px-6 py-3 border border-slate-700 hover:border-ember-500 text-slate-300 hover:text-ember-500 font-semibold rounded-lg transition-all">
206
219
  Read Docs →
207
220
  </a>
208
221
  </div>
209
222
  </section>
210
223
 
211
- <section className="features">
212
- <div className="feature-card">
213
- <div className="feature-icon">⚡</div>
214
- <h3>Lightning Fast</h3>
215
- <p>Sub-10KB runtime with tree-shakeable architecture</p>
224
+ <section className="grid md:grid-cols-3 gap-6">
225
+ <div className="p-6 rounded-xl border border-slate-800 bg-slate-800/30 hover:border-ember-500/50 transition-colors group">
226
+ <div className="text-3xl mb-4">⚡</div>
227
+ <h3 className="text-lg font-semibold mb-2">Lightning Fast</h3>
228
+ <p className="text-slate-400">Sub-10KB runtime with tree-shakeable architecture</p>
216
229
  </div>
217
- <div className="feature-card">
218
- <div className="feature-icon">🔷</div>
219
- <h3>TypeScript First</h3>
220
- <p>Full type safety with intelligent autocomplete</p>
230
+ <div className="p-6 rounded-xl border border-slate-800 bg-slate-800/30 hover:border-ember-500/50 transition-colors group">
231
+ <div className="text-3xl mb-4">🔷</div>
232
+ <h3 className="text-lg font-semibold mb-2">TypeScript First</h3>
233
+ <p className="text-slate-400">Full type safety with intelligent autocomplete</p>
221
234
  </div>
222
- <div className="feature-card">
223
- <div className="feature-icon">🛤️</div>
224
- <h3>File-Based Routing</h3>
225
- <p>Routes automatically created from your file structure</p>
235
+ <div className="p-6 rounded-xl border border-slate-800 bg-slate-800/30 hover:border-ember-500/50 transition-colors group">
236
+ <div className="text-3xl mb-4">🛤️</div>
237
+ <h3 className="text-lg font-semibold mb-2">File-Based Routing</h3>
238
+ <p className="text-slate-400">Routes automatically created from your file structure</p>
226
239
  </div>
227
240
  </section>
228
241
 
229
- <section className="counter">
230
- <h2>Try the Counter</h2>
231
- <div className="counter-display">
242
+ <section className="p-8 rounded-2xl bg-gradient-to-br from-slate-800/50 to-slate-900/50 border border-slate-700 text-center">
243
+ <h2 className="text-2xl font-bold mb-6">Interactive Counter Demo</h2>
244
+ <div className="flex items-center justify-center gap-6">
232
245
  <button
233
- className="counter-btn"
246
+ className="w-12 h-12 rounded-lg bg-slate-800 border border-slate-700 hover:bg-ember-500 hover:border-ember-500 text-ember-500 hover:text-white text-xl transition-all"
234
247
  onClick={() => count.value--}
235
248
  >
236
249
 
237
250
  </button>
238
- <span className="counter-value">{count}</span>
251
+ <span className="text-5xl font-bold tabular-nums min-w-[80px]">{count}</span>
239
252
  <button
240
- className="counter-btn"
253
+ className="w-12 h-12 rounded-lg bg-slate-800 border border-slate-700 hover:bg-ember-500 hover:border-ember-500 text-ember-500 hover:text-white text-xl transition-all"
241
254
  onClick={() => count.value++}
242
255
  >
243
256
  +
244
257
  </button>
245
258
  </div>
259
+ <p className="text-slate-500 mt-4 text-sm">Try clicking the buttons!</p>
260
+ </section>
261
+
262
+ <section className="text-center py-8">
263
+ <div className="inline-flex items-center gap-3 px-4 py-2 rounded-full bg-ember-500/10 border border-ember-500/20 text-ember-400">
264
+ <span className="w-2 h-2 rounded-full bg-ember-500 animate-pulse"></span>
265
+ <span className="text-sm font-medium">Ready to build something amazing?</span>
266
+ </div>
246
267
  </section>
247
268
  </div>
248
269
  );
@@ -254,303 +275,37 @@ import { Head } from '@emberkit/core';
254
275
 
255
276
  const AboutPage: RouteComponent = () => {
256
277
  return (
257
- <div className="about">
278
+ <div className="max-w-3xl mx-auto px-6 py-16">
258
279
  <Head>
259
280
  <title>About - {{name}}</title>
260
281
  </Head>
261
- <div className="about-content">
262
- <h1>About {{name}}</h1>
263
- <p>
264
- EmberKit is a minimalist TypeScript-first JSX framework built for speed and simplicity.
265
- It combines the best of modern frontend development with a lightweight runtime.
266
- </p>
267
- <div className="about-features">
268
- <div className="about-feature">
269
- <span className="feature-badge">SPA & SSR</span>
270
- <span>Works in both modes</span>
271
- </div>
272
- <div className="about-feature">
273
- <span className="feature-badge">Zero Config</span>
274
- <span>Sensible defaults</span>
275
- </div>
276
- <div className="about-feature">
277
- <span className="feature-badge">HMR</span>
278
- <span>Hot module replacement</span>
279
- </div>
282
+ <h1 className="text-4xl font-bold mb-6">About {{name}}</h1>
283
+ <p className="text-slate-400 text-lg leading-relaxed mb-8">
284
+ EmberKit is a minimalist TypeScript-first JSX framework built for speed and simplicity.
285
+ It combines the best of modern frontend development with a lightweight runtime.
286
+ </p>
287
+ <div className="grid sm:grid-cols-3 gap-4 mb-8">
288
+ <div className="p-4 rounded-lg bg-slate-800/50 border border-slate-700">
289
+ <span className="text-ember-500 text-sm font-semibold">SPA & SSR</span>
290
+ <p className="text-slate-500 text-sm mt-1">Works in both modes</p>
291
+ </div>
292
+ <div className="p-4 rounded-lg bg-slate-800/50 border border-slate-700">
293
+ <span className="text-ember-500 text-sm font-semibold">Zero Config</span>
294
+ <p className="text-slate-500 text-sm mt-1">Sensible defaults</p>
295
+ </div>
296
+ <div className="p-4 rounded-lg bg-slate-800/50 border border-slate-700">
297
+ <span className="text-ember-500 text-sm font-semibold">HMR</span>
298
+ <p className="text-slate-500 text-sm mt-1">Hot module replacement</p>
280
299
  </div>
281
- <a href="/" className="back-link">← Back to Home</a>
282
300
  </div>
301
+ <a href="/" className="inline-flex items-center gap-2 text-ember-500 hover:text-ember-400 font-medium">
302
+ ← Back to Home
303
+ </a>
283
304
  </div>
284
305
  );
285
306
  };
286
307
 
287
308
  export default AboutPage;`,
288
- "src/styles.css": `.app {
289
- min-height: 100vh;
290
- display: flex;
291
- flex-direction: column;
292
- }
293
-
294
- .header {
295
- display: flex;
296
- justify-content: space-between;
297
- align-items: center;
298
- padding: 1.5rem 2rem;
299
- border-bottom: 1px solid #1e293b;
300
- }
301
-
302
- .logo {
303
- display: flex;
304
- align-items: center;
305
- gap: 0.5rem;
306
- }
307
-
308
- .logo-icon {
309
- font-size: 1.5rem;
310
- }
311
-
312
- .logo-text {
313
- font-weight: 700;
314
- font-size: 1.25rem;
315
- background: linear-gradient(135deg, #f97316, #fb923c);
316
- -webkit-background-clip: text;
317
- -webkit-text-fill-color: transparent;
318
- background-clip: text;
319
- }
320
-
321
- .nav {
322
- display: flex;
323
- gap: 1.5rem;
324
- }
325
-
326
- .nav-link {
327
- color: #94a3b8;
328
- font-weight: 500;
329
- transition: color 0.2s;
330
- }
331
-
332
- .nav-link:hover {
333
- color: #f97316;
334
- }
335
-
336
- .main {
337
- flex: 1;
338
- max-width: 1200px;
339
- width: 100%;
340
- margin: 0 auto;
341
- padding: 3rem 2rem;
342
- }
343
-
344
- .footer {
345
- padding: 2rem;
346
- text-align: center;
347
- border-top: 1px solid #1e293b;
348
- color: #64748b;
349
- }
350
-
351
- .footer-link {
352
- color: #f97316;
353
- }
354
-
355
- .home {
356
- display: flex;
357
- flex-direction: column;
358
- gap: 4rem;
359
- }
360
-
361
- .hero {
362
- text-align: center;
363
- padding: 2rem 0;
364
- }
365
-
366
- .hero-title {
367
- font-size: 3rem;
368
- font-weight: 800;
369
- margin-bottom: 1.5rem;
370
- line-height: 1.1;
371
- }
372
-
373
- .gradient-text {
374
- background: linear-gradient(135deg, #f97316, #fb923c, #fdba74);
375
- -webkit-background-clip: text;
376
- -webkit-text-fill-color: transparent;
377
- background-clip: text;
378
- }
379
-
380
- .hero-desc {
381
- font-size: 1.25rem;
382
- color: #94a3b8;
383
- max-width: 600px;
384
- margin: 0 auto 2rem;
385
- line-height: 1.6;
386
- }
387
-
388
- .hero-actions {
389
- display: flex;
390
- gap: 1rem;
391
- justify-content: center;
392
- }
393
-
394
- .btn {
395
- display: inline-block;
396
- padding: 0.875rem 1.75rem;
397
- border-radius: 0.5rem;
398
- font-weight: 600;
399
- transition: all 0.2s;
400
- }
401
-
402
- .btn-primary {
403
- background: #f97316;
404
- color: white;
405
- }
406
-
407
- .btn-primary:hover {
408
- background: #ea580c;
409
- transform: translateY(-1px);
410
- }
411
-
412
- .btn-secondary {
413
- background: #1e293b;
414
- color: #e2e8f0;
415
- border: 1px solid #334155;
416
- }
417
-
418
- .btn-secondary:hover {
419
- background: #334155;
420
- border-color: #475569;
421
- }
422
-
423
- .features {
424
- display: grid;
425
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
426
- gap: 1.5rem;
427
- }
428
-
429
- .feature-card {
430
- background: #1e293b;
431
- border: 1px solid #334155;
432
- border-radius: 0.75rem;
433
- padding: 1.5rem;
434
- transition: all 0.2s;
435
- }
436
-
437
- .feature-card:hover {
438
- border-color: #f97316;
439
- transform: translateY(-2px);
440
- }
441
-
442
- .feature-icon {
443
- font-size: 2rem;
444
- margin-bottom: 1rem;
445
- }
446
-
447
- .feature-card h3 {
448
- font-size: 1.125rem;
449
- font-weight: 600;
450
- margin-bottom: 0.5rem;
451
- }
452
-
453
- .feature-card p {
454
- color: #64748b;
455
- font-size: 0.875rem;
456
- }
457
-
458
- .counter {
459
- text-align: center;
460
- padding: 2rem;
461
- background: #1e293b;
462
- border-radius: 1rem;
463
- }
464
-
465
- .counter h2 {
466
- margin-bottom: 1.5rem;
467
- font-size: 1.5rem;
468
- }
469
-
470
- .counter-display {
471
- display: flex;
472
- align-items: center;
473
- justify-content: center;
474
- gap: 1.5rem;
475
- }
476
-
477
- .counter-btn {
478
- width: 48px;
479
- height: 48px;
480
- border-radius: 0.5rem;
481
- border: 1px solid #334155;
482
- background: #0f172a;
483
- color: #f97316;
484
- font-size: 1.5rem;
485
- cursor: pointer;
486
- transition: all 0.2s;
487
- }
488
-
489
- .counter-btn:hover {
490
- background: #f97316;
491
- color: white;
492
- border-color: #f97316;
493
- }
494
-
495
- .counter-value {
496
- font-size: 2.5rem;
497
- font-weight: 700;
498
- min-width: 60px;
499
- }
500
-
501
- .about {
502
- max-width: 700px;
503
- margin: 0 auto;
504
- }
505
-
506
- .about h1 {
507
- font-size: 2rem;
508
- font-weight: 700;
509
- margin-bottom: 1.5rem;
510
- }
511
-
512
- .about > div > p {
513
- color: #94a3b8;
514
- font-size: 1.125rem;
515
- line-height: 1.7;
516
- margin-bottom: 2rem;
517
- }
518
-
519
- .about-features {
520
- display: flex;
521
- gap: 1rem;
522
- flex-wrap: wrap;
523
- margin-bottom: 2rem;
524
- }
525
-
526
- .about-feature {
527
- display: flex;
528
- align-items: center;
529
- gap: 0.5rem;
530
- background: #1e293b;
531
- padding: 0.75rem 1rem;
532
- border-radius: 0.5rem;
533
- font-size: 0.875rem;
534
- }
535
-
536
- .feature-badge {
537
- background: #f97316;
538
- color: white;
539
- padding: 0.125rem 0.5rem;
540
- border-radius: 0.25rem;
541
- font-size: 0.75rem;
542
- font-weight: 600;
543
- }
544
-
545
- .back-link {
546
- display: inline-block;
547
- color: #f97316;
548
- font-weight: 500;
549
- }
550
-
551
- .back-link:hover {
552
- text-decoration: underline;
553
- }`,
554
309
  };
555
310
  export async function create(options) {
556
311
  printHeader();
@@ -660,15 +415,6 @@ const withUiTemplate = {
660
415
  "include": ["src"],
661
416
  "exclude": ["node_modules", "dist"]
662
417
  }`,
663
- "emberkit.config.ts": `import { defineConfig } from '@emberkit/core';
664
-
665
- export default defineConfig({
666
- mode: 'spa',
667
- build: {
668
- outDir: 'dist',
669
- target: 'esnext',
670
- },
671
- });`,
672
418
  "vite.config.ts": `import { defineConfig } from 'vite';
673
419
  import { emberkitVitePlugin } from '@emberkit/core/vite-plugin';
674
420
  import tailwindcss from '@tailwindcss/vite';
@@ -679,9 +425,6 @@ export default defineConfig({
679
425
  port: 3000,
680
426
  host: 'localhost',
681
427
  },
682
- esbuild: {
683
- jsxImportSource: '@emberkit/core',
684
- },
685
428
  });`,
686
429
  "index.html": `<!DOCTYPE html>
687
430
  <html lang="en">
@@ -28,6 +28,25 @@ export function getPackageManager() {
28
28
  return "pnpm";
29
29
  if (userAgent.startsWith("yarn"))
30
30
  return "yarn";
31
+ if (userAgent.startsWith("npm"))
32
+ return "npm";
33
+ const { existsSync } = require("fs");
34
+ const { platform } = require("os");
35
+ try {
36
+ if (platform() === "win32") {
37
+ if (existsSync("C:\\Program Files\\pnpm\\pnpm.exe") || existsSync(process.env.LOCALAPPDATA + "\\pnpm\\pnpm.exe")) {
38
+ return "pnpm";
39
+ }
40
+ }
41
+ else {
42
+ const { execSync } = require("child_process");
43
+ execSync("pnpm --version", { stdio: "ignore" });
44
+ return "pnpm";
45
+ }
46
+ }
47
+ catch {
48
+ return "npm";
49
+ }
31
50
  return "npm";
32
51
  }
33
52
  export function getInstallCommand() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emberkit/cli",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "CLI tool for EmberKit projects",