@emberkit/cli 0.2.4 → 0.2.5
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/commands/create.js +562 -14
- package/package.json +1 -1
package/dist/commands/create.js
CHANGED
|
@@ -1,8 +1,44 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
2
|
-
import {
|
|
2
|
+
import { resolve, join } from "path";
|
|
3
3
|
import { execSync } from "child_process";
|
|
4
|
-
import { starterFiles } from "../templates/starter.js";
|
|
5
4
|
import { getPackageManager, getInstallCommand } from "../utils/filesystem.js";
|
|
5
|
+
const RESET = "\x1b[0m";
|
|
6
|
+
const BOLD = "\x1b[1m";
|
|
7
|
+
const DIM = "\x1b[2m";
|
|
8
|
+
const BRIGHT_BLACK = "\x1b[90m";
|
|
9
|
+
const BRIGHT_RED = "\x1b[91m";
|
|
10
|
+
const BRIGHT_GREEN = "\x1b[92m";
|
|
11
|
+
const BRIGHT_BLUE = "\x1b[94m";
|
|
12
|
+
const BRIGHT_CYAN = "\x1b[96m";
|
|
13
|
+
const BRIGHT_WHITE = "\x1b[97m";
|
|
14
|
+
const ORANGE_BG = "\x1b[48;5;208m";
|
|
15
|
+
function printHeader() {
|
|
16
|
+
const header = `
|
|
17
|
+
${BRIGHT_BLACK}╭─────────────────────────────────────────────────────╮${RESET}
|
|
18
|
+
${BRIGHT_BLACK}│${RESET} ${ORANGE_BG}${BRIGHT_BLACK} EmberKit ${RESET} ${BRIGHT_BLACK}│${RESET}
|
|
19
|
+
${BRIGHT_BLACK}│${RESET} ${DIM}A minimalist TypeScript-first JSX framework${RESET} ${BRIGHT_BLACK}│${RESET}
|
|
20
|
+
${BRIGHT_BLACK}╰─────────────────────────────────────────────────────╯${RESET}
|
|
21
|
+
`;
|
|
22
|
+
console.log(header);
|
|
23
|
+
}
|
|
24
|
+
function printStep(step, total, message) {
|
|
25
|
+
void total;
|
|
26
|
+
const numStr = BRIGHT_CYAN + String(step).padStart(2, "0") + RESET;
|
|
27
|
+
const bar = DIM + "━".repeat(40 - message.length) + RESET;
|
|
28
|
+
console.log(` ${numStr} ${BRIGHT_WHITE + message + RESET} ${bar}`);
|
|
29
|
+
}
|
|
30
|
+
function printSuccess(message) {
|
|
31
|
+
const check = BRIGHT_GREEN + "✓" + RESET;
|
|
32
|
+
console.log(`\n ${check} ${BRIGHT_GREEN + message + RESET}\n`);
|
|
33
|
+
}
|
|
34
|
+
function printError(message) {
|
|
35
|
+
const err = BRIGHT_RED + "✗" + RESET;
|
|
36
|
+
console.log(`\n ${err} ${BRIGHT_RED + message + RESET}\n`);
|
|
37
|
+
}
|
|
38
|
+
function printInfo(message) {
|
|
39
|
+
const info = BRIGHT_BLUE + "›" + RESET;
|
|
40
|
+
console.log(` ${info} ${DIM + message + RESET}`);
|
|
41
|
+
}
|
|
6
42
|
function formatTemplate(template, vars) {
|
|
7
43
|
let result = template;
|
|
8
44
|
for (const [key, value] of Object.entries(vars)) {
|
|
@@ -20,22 +56,524 @@ function getNpmPackageName(name) {
|
|
|
20
56
|
const kebab = toKebabCase(name);
|
|
21
57
|
return kebab.startsWith("@") ? kebab : kebab.replace(/^emberkit-/, "");
|
|
22
58
|
}
|
|
59
|
+
const starterFiles = {
|
|
60
|
+
"package.json": `{
|
|
61
|
+
"name": "{{name}}",
|
|
62
|
+
"version": "0.1.0",
|
|
63
|
+
"private": true,
|
|
64
|
+
"type": "module",
|
|
65
|
+
"scripts": {
|
|
66
|
+
"dev": "emberkit dev",
|
|
67
|
+
"build": "emberkit build",
|
|
68
|
+
"preview": "emberkit preview",
|
|
69
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
70
|
+
"format": "prettier --write \\"src/**/*.{ts,tsx}\\""
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@emberkit/core": "^0.2.4"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@emberkit/cli": "^0.2.4",
|
|
77
|
+
"typescript": "^5.7.0",
|
|
78
|
+
"vite": "^6.0.0"
|
|
79
|
+
}
|
|
80
|
+
}`,
|
|
81
|
+
"tsconfig.json": `{
|
|
82
|
+
"compilerOptions": {
|
|
83
|
+
"target": "ES2022",
|
|
84
|
+
"module": "ESNext",
|
|
85
|
+
"moduleResolution": "bundler",
|
|
86
|
+
"jsx": "react-jsx",
|
|
87
|
+
"jsxImportSource": "@emberkit/core",
|
|
88
|
+
"strict": true,
|
|
89
|
+
"esModuleInterop": true,
|
|
90
|
+
"skipLibCheck": true,
|
|
91
|
+
"forceConsistentCasingInFileNames": true,
|
|
92
|
+
"resolveJsonModule": true,
|
|
93
|
+
"isolatedModules": true,
|
|
94
|
+
"noEmit": true,
|
|
95
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
96
|
+
"paths": {
|
|
97
|
+
"@/*": ["./src/*"]
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"include": ["src"],
|
|
101
|
+
"exclude": ["node_modules", "dist"]
|
|
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
|
+
"vite.config.ts": `import { defineConfig } from 'vite';
|
|
113
|
+
import { emberkitVitePlugin } from '@emberkit/core/vite-plugin';
|
|
114
|
+
|
|
115
|
+
export default defineConfig({
|
|
116
|
+
plugins: [emberkitVitePlugin()],
|
|
117
|
+
server: {
|
|
118
|
+
port: 3000,
|
|
119
|
+
host: 'localhost',
|
|
120
|
+
},
|
|
121
|
+
esbuild: {
|
|
122
|
+
jsxImportSource: '@emberkit/core',
|
|
123
|
+
},
|
|
124
|
+
});`,
|
|
125
|
+
"index.html": `<!DOCTYPE html>
|
|
126
|
+
<html lang="en">
|
|
127
|
+
<head>
|
|
128
|
+
<meta charset="UTF-8">
|
|
129
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
130
|
+
<title>{{name}}</title>
|
|
131
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
132
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
133
|
+
<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
|
+
</head>
|
|
140
|
+
<body>
|
|
141
|
+
<div id="app"></div>
|
|
142
|
+
<script type="module" src="/src/index.tsx"></script>
|
|
143
|
+
</body>
|
|
144
|
+
</html>`,
|
|
145
|
+
"src/index.tsx": `import { render } from '@emberkit/core';
|
|
146
|
+
import App from './routes/_layout';
|
|
147
|
+
|
|
148
|
+
const root = document.getElementById('app');
|
|
149
|
+
|
|
150
|
+
if (root) {
|
|
151
|
+
render(App, root);
|
|
152
|
+
}`,
|
|
153
|
+
"src/routes/_layout.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
154
|
+
import { Head } from '@emberkit/core';
|
|
155
|
+
|
|
156
|
+
const Layout: RouteComponent = ({ children }) => {
|
|
157
|
+
return (
|
|
158
|
+
<>
|
|
159
|
+
<Head>
|
|
160
|
+
<title>{{name}}</title>
|
|
161
|
+
<meta name="description" content="Built with EmberKit" />
|
|
162
|
+
</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>
|
|
168
|
+
</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
|
+
</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>
|
|
178
|
+
</footer>
|
|
179
|
+
</div>
|
|
180
|
+
</>
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export default Layout;`,
|
|
185
|
+
"src/routes/index.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
186
|
+
import { signal } from '@emberkit/core';
|
|
187
|
+
|
|
188
|
+
const HomePage: RouteComponent = () => {
|
|
189
|
+
const count = signal(0);
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<div className="home">
|
|
193
|
+
<section className="hero">
|
|
194
|
+
<h1 className="hero-title">
|
|
195
|
+
Welcome to <span className="gradient-text">{{name}}</span>
|
|
196
|
+
</h1>
|
|
197
|
+
<p className="hero-desc">
|
|
198
|
+
A minimalist TypeScript-first JSX framework built for speed and simplicity.
|
|
199
|
+
Get started in seconds with hot module replacement and zero-config routing.
|
|
200
|
+
</p>
|
|
201
|
+
<div className="hero-actions">
|
|
202
|
+
<a href="/about" className="btn btn-primary">
|
|
203
|
+
Learn More
|
|
204
|
+
</a>
|
|
205
|
+
<a href="https://emberkit.dev/docs" target="_blank" className="btn btn-secondary">
|
|
206
|
+
Read Docs →
|
|
207
|
+
</a>
|
|
208
|
+
</div>
|
|
209
|
+
</section>
|
|
210
|
+
|
|
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>
|
|
216
|
+
</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>
|
|
221
|
+
</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>
|
|
226
|
+
</div>
|
|
227
|
+
</section>
|
|
228
|
+
|
|
229
|
+
<section className="counter">
|
|
230
|
+
<h2>Try the Counter</h2>
|
|
231
|
+
<div className="counter-display">
|
|
232
|
+
<button
|
|
233
|
+
className="counter-btn"
|
|
234
|
+
onClick={() => count.value--}
|
|
235
|
+
>
|
|
236
|
+
−
|
|
237
|
+
</button>
|
|
238
|
+
<span className="counter-value">{count}</span>
|
|
239
|
+
<button
|
|
240
|
+
className="counter-btn"
|
|
241
|
+
onClick={() => count.value++}
|
|
242
|
+
>
|
|
243
|
+
+
|
|
244
|
+
</button>
|
|
245
|
+
</div>
|
|
246
|
+
</section>
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export default HomePage;`,
|
|
252
|
+
"src/routes/about.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
253
|
+
import { Head } from '@emberkit/core';
|
|
254
|
+
|
|
255
|
+
const AboutPage: RouteComponent = () => {
|
|
256
|
+
return (
|
|
257
|
+
<div className="about">
|
|
258
|
+
<Head>
|
|
259
|
+
<title>About - {{name}}</title>
|
|
260
|
+
</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>
|
|
280
|
+
</div>
|
|
281
|
+
<a href="/" className="back-link">← Back to Home</a>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
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
|
+
};
|
|
23
555
|
export async function create(options) {
|
|
556
|
+
printHeader();
|
|
24
557
|
const { name, noInstall = false } = options;
|
|
25
558
|
const directory = options.directory ?? toKebabCase(name);
|
|
26
559
|
const targetDir = resolve(process.cwd(), directory);
|
|
27
|
-
|
|
560
|
+
const templateId = options.template || "basic";
|
|
561
|
+
printStep(1, 3, "Collecting project info");
|
|
562
|
+
console.log(` ${DIM}Project name:${RESET} ${BRIGHT_WHITE + name + RESET}`);
|
|
563
|
+
console.log(` ${DIM}Directory:${RESET} ${BRIGHT_WHITE + directory + RESET}`);
|
|
564
|
+
console.log(` ${DIM}Template:${RESET} ${BRIGHT_WHITE + templateId + RESET}\n`);
|
|
28
565
|
if (existsSync(targetDir)) {
|
|
29
|
-
|
|
566
|
+
printError(`Directory "${directory}" already exists.`);
|
|
30
567
|
process.exit(1);
|
|
31
568
|
}
|
|
569
|
+
printStep(2, 3, "Scaffolding project");
|
|
570
|
+
printInfo(`Creating ${directory}/`);
|
|
571
|
+
mkdirSync(targetDir, { recursive: true });
|
|
32
572
|
const templateVars = {
|
|
33
573
|
name,
|
|
34
574
|
packageName: getNpmPackageName(name),
|
|
35
575
|
kebabName: toKebabCase(name),
|
|
36
576
|
};
|
|
37
|
-
console.log(` Creating project in ${targetDir}...`);
|
|
38
|
-
mkdirSync(targetDir, { recursive: true });
|
|
39
577
|
for (const [filePath, content] of Object.entries(starterFiles)) {
|
|
40
578
|
const fullPath = join(targetDir, filePath);
|
|
41
579
|
const dir = join(targetDir, filePath.split("/").slice(0, -1).join("/"));
|
|
@@ -43,24 +581,34 @@ export async function create(options) {
|
|
|
43
581
|
mkdirSync(dir, { recursive: true });
|
|
44
582
|
}
|
|
45
583
|
writeFileSync(fullPath, formatTemplate(content, templateVars), "utf-8");
|
|
584
|
+
printInfo(`Created ${filePath}`);
|
|
46
585
|
}
|
|
47
|
-
|
|
586
|
+
printSuccess("Project scaffolded");
|
|
48
587
|
if (!noInstall) {
|
|
588
|
+
printStep(3, 3, "Installing dependencies");
|
|
49
589
|
const pm = getPackageManager();
|
|
50
590
|
const installCmd = getInstallCommand();
|
|
51
|
-
console.log(`
|
|
591
|
+
console.log(` ${DIM}Using:${RESET} ${BRIGHT_CYAN + pm + RESET}\n`);
|
|
52
592
|
try {
|
|
53
593
|
execSync(installCmd, { cwd: targetDir, stdio: "inherit" });
|
|
54
|
-
|
|
594
|
+
printSuccess("Dependencies installed");
|
|
55
595
|
}
|
|
56
596
|
catch {
|
|
57
|
-
|
|
597
|
+
printError("Failed to install dependencies");
|
|
598
|
+
console.log(` ${DIM}Run "${installCmd}" manually in ${directory}/${RESET}\n`);
|
|
58
599
|
}
|
|
59
600
|
}
|
|
60
|
-
console.log(
|
|
61
|
-
console.log(
|
|
601
|
+
console.log(`\n${BRIGHT_WHITE}╭────────────────────────────────────────╮${RESET}`);
|
|
602
|
+
console.log(`${BRIGHT_WHITE}│${RESET} ${BRIGHT_GREEN + BOLD}Success!${RESET} Your project is ready. ${BRIGHT_WHITE}│${RESET}`);
|
|
603
|
+
console.log(`${BRIGHT_WHITE}╰────────────────────────────────────────╯${RESET}\n`);
|
|
604
|
+
console.log(` ${DIM}To start development:${RESET}`);
|
|
605
|
+
console.log(` ${BRIGHT_CYAN}cd${RESET} ${directory}`);
|
|
62
606
|
if (noInstall) {
|
|
63
|
-
console.log(` ${getInstallCommand()}`);
|
|
607
|
+
console.log(` ${BRIGHT_CYAN}${getInstallCommand()}${RESET}`);
|
|
64
608
|
}
|
|
65
|
-
console.log(` emberkit dev\n`);
|
|
609
|
+
console.log(` ${BRIGHT_CYAN}emberkit dev${RESET}\n`);
|
|
610
|
+
console.log(` ${DIM}To build for production:${RESET}`);
|
|
611
|
+
console.log(` ${BRIGHT_CYAN}emberkit build${RESET}\n`);
|
|
612
|
+
console.log(` ${DIM}To preview the build:${RESET}`);
|
|
613
|
+
console.log(` ${BRIGHT_CYAN}emberkit preview${RESET}\n`);
|
|
66
614
|
}
|