@emberkit/cli 0.6.1-alpha.9 → 0.6.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/cli-package-version.js +10 -0
- package/dist/cli.js +3 -2
- package/dist/emberkit-package-versions.js +10 -0
- package/dist/templates/project-templates/_shared/base.js +7 -4
- package/dist/templates/project-templates/blog/blog.js +3 -9
- package/dist/templates/project-templates/dashboard/dashboard.js +22 -22
- package/dist/templates/project-templates/saas/saas.js +39 -39
- package/dist/templates/project-templates/starter-kit/with-ui.js +2 -2
- package/package.json +6 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/** `packages/cli/package.json` version (works when running compiled output in `dist/`). */
|
|
5
|
+
export function getCliPackageVersion() {
|
|
6
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const pkgPath = join(here, "../package.json");
|
|
8
|
+
const raw = readFileSync(pkgPath, "utf8");
|
|
9
|
+
return JSON.parse(raw).version;
|
|
10
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import inquirer from "inquirer";
|
|
2
|
+
import { getCliPackageVersion } from "./cli-package-version.js";
|
|
2
3
|
import { dev } from "./commands/dev.js";
|
|
3
4
|
import { build } from "./commands/build.js";
|
|
4
5
|
import { preview } from "./commands/preview.js";
|
|
@@ -29,7 +30,7 @@ export async function runCLI(args) {
|
|
|
29
30
|
break;
|
|
30
31
|
case "--version":
|
|
31
32
|
case "-v":
|
|
32
|
-
console.log(
|
|
33
|
+
console.log(`EmberKit CLI v${getCliPackageVersion()}`);
|
|
33
34
|
break;
|
|
34
35
|
case "--help":
|
|
35
36
|
case "-h":
|
|
@@ -43,7 +44,7 @@ export async function runCLI(args) {
|
|
|
43
44
|
}
|
|
44
45
|
function showHelp() {
|
|
45
46
|
console.log(`
|
|
46
|
-
🔥 EmberKit CLI
|
|
47
|
+
🔥 EmberKit CLI v${getCliPackageVersion()}
|
|
47
48
|
|
|
48
49
|
Usage: emberkit <command> [options]
|
|
49
50
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Semver ranges for @emberkit/* packages written into generated projects.
|
|
2
|
+
// When releasing libraries, bump these to match packages/*/package.json "version".
|
|
3
|
+
export const EMBERKIT_PACKAGE_VERSIONS = {
|
|
4
|
+
core: "^0.2.6",
|
|
5
|
+
ui: "^0.3.0",
|
|
6
|
+
icons: "^0.2.3",
|
|
7
|
+
cli: "^0.6.1",
|
|
8
|
+
edge: "^0.2.3",
|
|
9
|
+
tsconfig: "^0.2.1",
|
|
10
|
+
};
|
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
* Shared builders for project template boilerplate.
|
|
3
3
|
* Each template composes from these instead of duplicating identical config files.
|
|
4
4
|
*/
|
|
5
|
+
import { EMBERKIT_PACKAGE_VERSIONS as V } from "../../../emberkit-package-versions.js";
|
|
5
6
|
export function buildPackageJson(options = {}) {
|
|
6
7
|
const { hasTailwind = false, hasUI = false } = options;
|
|
7
|
-
const deps = { "@emberkit/core":
|
|
8
|
-
if (hasUI)
|
|
9
|
-
deps["@emberkit/ui"] =
|
|
8
|
+
const deps = { "@emberkit/core": V.core };
|
|
9
|
+
if (hasUI) {
|
|
10
|
+
deps["@emberkit/ui"] = V.ui;
|
|
11
|
+
deps["@emberkit/icons"] = V.icons;
|
|
12
|
+
}
|
|
10
13
|
const devDeps = {
|
|
11
|
-
"@emberkit/cli":
|
|
14
|
+
"@emberkit/cli": V.cli,
|
|
12
15
|
typescript: "^5.7.0",
|
|
13
16
|
vite: "^6.0.0",
|
|
14
17
|
};
|
|
@@ -156,7 +156,7 @@ const HomePage: RouteComponent = () => {
|
|
|
156
156
|
};
|
|
157
157
|
|
|
158
158
|
export default HomePage;`,
|
|
159
|
-
"src/routes/[slug].tsx": `import type {
|
|
159
|
+
"src/routes/[slug].tsx": `import type { RouteParams } from '@emberkit/core';
|
|
160
160
|
import { Head } from '@emberkit/core';
|
|
161
161
|
|
|
162
162
|
interface PostData {
|
|
@@ -212,11 +212,7 @@ const posts: Record<string, PostData> = {
|
|
|
212
212
|
},
|
|
213
213
|
};
|
|
214
214
|
|
|
215
|
-
|
|
216
|
-
slug: string;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const PostPage: RouteComponent<Params> = ({ params }: RouteParams<Params>) => {
|
|
215
|
+
export default function PostPage({ params }: RouteParams<{ slug: string }>) {
|
|
220
216
|
const post = posts[params.slug];
|
|
221
217
|
|
|
222
218
|
if (!post) {
|
|
@@ -250,9 +246,7 @@ const PostPage: RouteComponent<Params> = ({ params }: RouteParams<Params>) => {
|
|
|
250
246
|
</article>
|
|
251
247
|
</>
|
|
252
248
|
);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
export default PostPage;`,
|
|
249
|
+
}`,
|
|
256
250
|
"src/routes/about.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
257
251
|
import { Head } from '@emberkit/core';
|
|
258
252
|
|
|
@@ -28,11 +28,11 @@ body {
|
|
|
28
28
|
@apply bg-gray-50 text-gray-900 font-sans;
|
|
29
29
|
}`,
|
|
30
30
|
"src/routes/_layout.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
31
|
-
import {
|
|
31
|
+
import { createSignal } from '@emberkit/core';
|
|
32
32
|
import { Sidebar, Header } from '@emberkit/ui';
|
|
33
33
|
|
|
34
34
|
const Layout: RouteComponent = ({ children }) => {
|
|
35
|
-
const sidebarOpen =
|
|
35
|
+
const [sidebarOpen, setSidebarOpen] = createSignal(true);
|
|
36
36
|
|
|
37
37
|
const sidebarItems = [
|
|
38
38
|
{ label: 'Dashboard', href: '/dashboard', icon: 'grid' },
|
|
@@ -47,15 +47,15 @@ const Layout: RouteComponent = ({ children }) => {
|
|
|
47
47
|
<Sidebar
|
|
48
48
|
logo={<span className="font-bold text-lg">⚡ {{name}}</span>}
|
|
49
49
|
items={sidebarItems}
|
|
50
|
-
collapsed={!sidebarOpen
|
|
51
|
-
onToggle={() => {
|
|
50
|
+
collapsed={!sidebarOpen()}
|
|
51
|
+
onToggle={() => { setSidebarOpen(!sidebarOpen()); }}
|
|
52
52
|
className="w-64"
|
|
53
53
|
/>
|
|
54
54
|
<div className="flex-1 flex flex-col min-w-0">
|
|
55
55
|
<Header
|
|
56
56
|
title="Dashboard"
|
|
57
57
|
user={{ name: 'User', avatar: '' }}
|
|
58
|
-
onMenuClick={() => {
|
|
58
|
+
onMenuClick={() => { setSidebarOpen(!sidebarOpen()); }}
|
|
59
59
|
/>
|
|
60
60
|
<main className="flex-1 p-6 overflow-auto">
|
|
61
61
|
{children}
|
|
@@ -162,7 +162,7 @@ const DashboardPage: RouteComponent = () => {
|
|
|
162
162
|
export default DashboardPage;`,
|
|
163
163
|
"src/routes/users.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
164
164
|
import { Card, Badge, Button, Input } from '@emberkit/ui';
|
|
165
|
-
import {
|
|
165
|
+
import { createSignal } from '@emberkit/core';
|
|
166
166
|
|
|
167
167
|
interface User {
|
|
168
168
|
id: number;
|
|
@@ -182,12 +182,12 @@ const users: User[] = [
|
|
|
182
182
|
];
|
|
183
183
|
|
|
184
184
|
const UsersPage: RouteComponent = () => {
|
|
185
|
-
const search =
|
|
185
|
+
const [search, setSearch] = createSignal('');
|
|
186
186
|
|
|
187
187
|
const filteredUsers = users.filter(
|
|
188
188
|
(u) =>
|
|
189
|
-
u.name.toLowerCase().includes(search.
|
|
190
|
-
u.email.toLowerCase().includes(search.
|
|
189
|
+
u.name.toLowerCase().includes(search().toLowerCase()) ||
|
|
190
|
+
u.email.toLowerCase().includes(search().toLowerCase())
|
|
191
191
|
);
|
|
192
192
|
|
|
193
193
|
const statusVariant = (status: User['status']) => {
|
|
@@ -212,8 +212,8 @@ const UsersPage: RouteComponent = () => {
|
|
|
212
212
|
<div className="mb-4">
|
|
213
213
|
<Input
|
|
214
214
|
placeholder="Search users..."
|
|
215
|
-
value={search
|
|
216
|
-
onInput={(e) => {
|
|
215
|
+
value={search()}
|
|
216
|
+
onInput={(e) => { setSearch(e.currentTarget.value); }}
|
|
217
217
|
className="max-w-sm"
|
|
218
218
|
/>
|
|
219
219
|
</div>
|
|
@@ -260,17 +260,17 @@ const UsersPage: RouteComponent = () => {
|
|
|
260
260
|
export default UsersPage;`,
|
|
261
261
|
"src/routes/settings.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
262
262
|
import { Card, Input, Button, Alert } from '@emberkit/ui';
|
|
263
|
-
import {
|
|
263
|
+
import { createSignal } from '@emberkit/core';
|
|
264
264
|
|
|
265
265
|
const SettingsPage: RouteComponent = () => {
|
|
266
|
-
const name =
|
|
267
|
-
const email =
|
|
268
|
-
const saved =
|
|
266
|
+
const [name, setName] = createSignal('John Doe');
|
|
267
|
+
const [email, setEmail] = createSignal('john@example.com');
|
|
268
|
+
const [saved, setSaved] = createSignal(false);
|
|
269
269
|
|
|
270
270
|
const handleSave = (e: Event) => {
|
|
271
271
|
e.preventDefault();
|
|
272
|
-
|
|
273
|
-
setTimeout(() => {
|
|
272
|
+
setSaved(true);
|
|
273
|
+
setTimeout(() => { setSaved(false); }, 3000);
|
|
274
274
|
};
|
|
275
275
|
|
|
276
276
|
return (
|
|
@@ -280,7 +280,7 @@ const SettingsPage: RouteComponent = () => {
|
|
|
280
280
|
<p className="text-gray-600 mt-1">Manage your account preferences.</p>
|
|
281
281
|
</div>
|
|
282
282
|
|
|
283
|
-
{saved
|
|
283
|
+
{saved() && (
|
|
284
284
|
<Alert variant="success">Settings saved successfully!</Alert>
|
|
285
285
|
)}
|
|
286
286
|
|
|
@@ -289,14 +289,14 @@ const SettingsPage: RouteComponent = () => {
|
|
|
289
289
|
<form onSubmit={handleSave} className="space-y-4">
|
|
290
290
|
<Input
|
|
291
291
|
label="Full Name"
|
|
292
|
-
value={name
|
|
293
|
-
onInput={(e) => {
|
|
292
|
+
value={name()}
|
|
293
|
+
onInput={(e) => { setName(e.currentTarget.value); }}
|
|
294
294
|
/>
|
|
295
295
|
<Input
|
|
296
296
|
label="Email"
|
|
297
297
|
type="email"
|
|
298
|
-
value={email
|
|
299
|
-
onInput={(e) => {
|
|
298
|
+
value={email()}
|
|
299
|
+
onInput={(e) => { setEmail(e.currentTarget.value); }}
|
|
300
300
|
/>
|
|
301
301
|
<div className="flex justify-end">
|
|
302
302
|
<Button variant="primary" type="submit">Save Changes</Button>
|
|
@@ -103,11 +103,11 @@ const Layout: RouteComponent = ({ children }) => {
|
|
|
103
103
|
|
|
104
104
|
export default Layout;`,
|
|
105
105
|
"src/routes/index.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
106
|
-
import {
|
|
106
|
+
import { createSignal } from '@emberkit/core';
|
|
107
107
|
import { Button, Card, Badge } from '@emberkit/ui';
|
|
108
108
|
|
|
109
109
|
const HomePage: RouteComponent = () => {
|
|
110
|
-
const annual =
|
|
110
|
+
const [annual, setAnnual] = createSignal(false);
|
|
111
111
|
|
|
112
112
|
const features = [
|
|
113
113
|
{ icon: '⚡', title: 'Lightning Fast', desc: 'Sub-10KB runtime with tree-shakeable architecture' },
|
|
@@ -121,8 +121,8 @@ const HomePage: RouteComponent = () => {
|
|
|
121
121
|
const plans = [
|
|
122
122
|
{
|
|
123
123
|
name: 'Starter',
|
|
124
|
-
price: annual
|
|
125
|
-
period: annual
|
|
124
|
+
price: annual() ? '$0' : '$0',
|
|
125
|
+
period: annual() ? '/year' : '/month',
|
|
126
126
|
desc: 'Perfect for side projects',
|
|
127
127
|
features: ['1 user', '5 projects', '1GB storage', 'Community support'],
|
|
128
128
|
cta: 'Get Started Free',
|
|
@@ -130,8 +130,8 @@ const HomePage: RouteComponent = () => {
|
|
|
130
130
|
},
|
|
131
131
|
{
|
|
132
132
|
name: 'Pro',
|
|
133
|
-
price: annual
|
|
134
|
-
period: annual
|
|
133
|
+
price: annual() ? '$199' : '$19',
|
|
134
|
+
period: annual() ? '/year' : '/month',
|
|
135
135
|
desc: 'For growing teams',
|
|
136
136
|
features: ['10 users', 'Unlimited projects', '50GB storage', 'Priority support', 'Analytics', 'API access'],
|
|
137
137
|
cta: 'Start Free Trial',
|
|
@@ -211,14 +211,14 @@ const HomePage: RouteComponent = () => {
|
|
|
211
211
|
<p className="text-lg text-slate-600 mb-8">Choose the plan that works for your team</p>
|
|
212
212
|
<div className="inline-flex items-center gap-3 p-1 bg-white rounded-lg border border-slate-200">
|
|
213
213
|
<button
|
|
214
|
-
className={\`px-4 py-2 text-sm font-medium rounded-md transition-colors \${!annual
|
|
215
|
-
onClick={() => {
|
|
214
|
+
className={\`px-4 py-2 text-sm font-medium rounded-md transition-colors \${!annual() ? 'bg-brand-600 text-white' : 'text-slate-600'}\`}
|
|
215
|
+
onClick={() => { setAnnual(false); }}
|
|
216
216
|
>
|
|
217
217
|
Monthly
|
|
218
218
|
</button>
|
|
219
219
|
<button
|
|
220
|
-
className={\`px-4 py-2 text-sm font-medium rounded-md transition-colors \${annual
|
|
221
|
-
onClick={() => {
|
|
220
|
+
className={\`px-4 py-2 text-sm font-medium rounded-md transition-colors \${annual() ? 'bg-brand-600 text-white' : 'text-slate-600'}\`}
|
|
221
|
+
onClick={() => { setAnnual(true); }}
|
|
222
222
|
>
|
|
223
223
|
Annual <span className="text-brand-600 ml-1 font-semibold">-20%</span>
|
|
224
224
|
</button>
|
|
@@ -280,19 +280,19 @@ export default HomePage;`,
|
|
|
280
280
|
"src/routes/login.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
281
281
|
import { Head } from '@emberkit/core';
|
|
282
282
|
import { Button, Input, Card } from '@emberkit/ui';
|
|
283
|
-
import {
|
|
283
|
+
import { createSignal } from '@emberkit/core';
|
|
284
284
|
|
|
285
285
|
const LoginPage: RouteComponent = () => {
|
|
286
|
-
const email =
|
|
287
|
-
const password =
|
|
288
|
-
const error =
|
|
286
|
+
const [email, setEmail] = createSignal('');
|
|
287
|
+
const [password, setPassword] = createSignal('');
|
|
288
|
+
const [error, setError] = createSignal<string | null>(null);
|
|
289
289
|
|
|
290
290
|
const handleSubmit = (e: Event) => {
|
|
291
291
|
e.preventDefault();
|
|
292
|
-
|
|
292
|
+
setError(null);
|
|
293
293
|
|
|
294
|
-
if (!email
|
|
295
|
-
|
|
294
|
+
if (!email() || !password()) {
|
|
295
|
+
setError('Please fill in all fields');
|
|
296
296
|
return;
|
|
297
297
|
}
|
|
298
298
|
|
|
@@ -315,18 +315,18 @@ const LoginPage: RouteComponent = () => {
|
|
|
315
315
|
label="Email"
|
|
316
316
|
type="email"
|
|
317
317
|
placeholder="you@example.com"
|
|
318
|
-
value={email
|
|
319
|
-
onInput={(e) => {
|
|
318
|
+
value={email()}
|
|
319
|
+
onInput={(e) => { setEmail(e.currentTarget.value); }}
|
|
320
320
|
/>
|
|
321
321
|
<Input
|
|
322
322
|
label="Password"
|
|
323
323
|
type="password"
|
|
324
324
|
placeholder="••••••••"
|
|
325
|
-
value={password
|
|
326
|
-
onInput={(e) => {
|
|
325
|
+
value={password()}
|
|
326
|
+
onInput={(e) => { setPassword(e.currentTarget.value); }}
|
|
327
327
|
/>
|
|
328
|
-
{error
|
|
329
|
-
<p className="text-red-500 text-sm">{error
|
|
328
|
+
{error() && (
|
|
329
|
+
<p className="text-red-500 text-sm">{error()}</p>
|
|
330
330
|
)}
|
|
331
331
|
<div className="flex items-center justify-between text-sm">
|
|
332
332
|
<label className="flex items-center gap-2">
|
|
@@ -350,20 +350,20 @@ export default LoginPage;`,
|
|
|
350
350
|
"src/routes/signup.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
351
351
|
import { Head } from '@emberkit/core';
|
|
352
352
|
import { Button, Input, Card } from '@emberkit/ui';
|
|
353
|
-
import {
|
|
353
|
+
import { createSignal } from '@emberkit/core';
|
|
354
354
|
|
|
355
355
|
const SignupPage: RouteComponent = () => {
|
|
356
|
-
const name =
|
|
357
|
-
const email =
|
|
358
|
-
const password =
|
|
359
|
-
const error =
|
|
356
|
+
const [name, setName] = createSignal('');
|
|
357
|
+
const [email, setEmail] = createSignal('');
|
|
358
|
+
const [password, setPassword] = createSignal('');
|
|
359
|
+
const [error, setError] = createSignal<string | null>(null);
|
|
360
360
|
|
|
361
361
|
const handleSubmit = (e: Event) => {
|
|
362
362
|
e.preventDefault();
|
|
363
|
-
|
|
363
|
+
setError(null);
|
|
364
364
|
|
|
365
|
-
if (!name
|
|
366
|
-
|
|
365
|
+
if (!name() || !email() || !password()) {
|
|
366
|
+
setError('Please fill in all fields');
|
|
367
367
|
return;
|
|
368
368
|
}
|
|
369
369
|
|
|
@@ -385,25 +385,25 @@ const SignupPage: RouteComponent = () => {
|
|
|
385
385
|
<Input
|
|
386
386
|
label="Full Name"
|
|
387
387
|
placeholder="John Doe"
|
|
388
|
-
value={name
|
|
389
|
-
onInput={(e) => {
|
|
388
|
+
value={name()}
|
|
389
|
+
onInput={(e) => { setName(e.currentTarget.value); }}
|
|
390
390
|
/>
|
|
391
391
|
<Input
|
|
392
392
|
label="Email"
|
|
393
393
|
type="email"
|
|
394
394
|
placeholder="you@example.com"
|
|
395
|
-
value={email
|
|
396
|
-
onInput={(e) => {
|
|
395
|
+
value={email()}
|
|
396
|
+
onInput={(e) => { setEmail(e.currentTarget.value); }}
|
|
397
397
|
/>
|
|
398
398
|
<Input
|
|
399
399
|
label="Password"
|
|
400
400
|
type="password"
|
|
401
401
|
placeholder="8+ characters"
|
|
402
|
-
value={password
|
|
403
|
-
onInput={(e) => {
|
|
402
|
+
value={password()}
|
|
403
|
+
onInput={(e) => { setPassword(e.currentTarget.value); }}
|
|
404
404
|
/>
|
|
405
|
-
{error
|
|
406
|
-
<p className="text-red-500 text-sm">{error
|
|
405
|
+
{error() && (
|
|
406
|
+
<p className="text-red-500 text-sm">{error()}</p>
|
|
407
407
|
)}
|
|
408
408
|
<p className="text-xs text-slate-500">
|
|
409
409
|
By signing up, you agree to our {' '}
|
|
@@ -103,7 +103,7 @@ const Layout: RouteComponent = ({ children }) => {
|
|
|
103
103
|
|
|
104
104
|
export default Layout;`,
|
|
105
105
|
"src/routes/index.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
106
|
-
import {
|
|
106
|
+
import { createSignal } from '@emberkit/core';
|
|
107
107
|
import {
|
|
108
108
|
Button,
|
|
109
109
|
Card,
|
|
@@ -116,7 +116,7 @@ import {
|
|
|
116
116
|
} from '@emberkit/ui';
|
|
117
117
|
|
|
118
118
|
const HomePage: RouteComponent = () => {
|
|
119
|
-
const activeTab =
|
|
119
|
+
const [activeTab, setActiveTab] = createSignal('features');
|
|
120
120
|
|
|
121
121
|
const features = [
|
|
122
122
|
{ icon: '⚡', title: 'Lightning Fast', desc: 'Sub-10KB runtime with tree-shakeable architecture' },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emberkit/cli",
|
|
3
|
-
"version": "0.6.1
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "CLI tool for EmberKit projects",
|
|
@@ -40,7 +40,11 @@
|
|
|
40
40
|
"inquirer": "^9.2.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@
|
|
43
|
+
"@eslint/js": "^10.0.1",
|
|
44
|
+
"@types/inquirer": "^9.0.3",
|
|
45
|
+
"eslint": "^10.0.0",
|
|
46
|
+
"eslint-plugin-perfectionist": "^5.9.0",
|
|
47
|
+
"typescript-eslint": "^8.59.3"
|
|
44
48
|
},
|
|
45
49
|
"scripts": {
|
|
46
50
|
"build": "tsc",
|