@emberkit/cli 0.5.1 → 0.6.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.
@@ -0,0 +1,12 @@
1
+ export const routeTemplate = `import type { RouteComponent } from '@emberkit/core';
2
+
3
+ const {{name}}: RouteComponent = () => {
4
+ return (
5
+ <div>
6
+ <h1>{{name}}</h1>
7
+ </div>
8
+ );
9
+ };
10
+
11
+ export default {{name}};
12
+ `;
@@ -0,0 +1,539 @@
1
+ export const saasTemplate = {
2
+ "package.json": `{
3
+ "name": "{{name}}",
4
+ "version": "0.1.0",
5
+ "private": true,
6
+ "type": "module",
7
+ "scripts": {
8
+ "dev": "emberkit dev",
9
+ "build": "emberkit build",
10
+ "preview": "emberkit preview"
11
+ },
12
+ "dependencies": {
13
+ "@emberkit/core": "^0.2.4",
14
+ "@emberkit/ui": "^0.2.3"
15
+ },
16
+ "devDependencies": {
17
+ "@emberkit/cli": "^0.2.4",
18
+ "typescript": "^5.7.0",
19
+ "vite": "^6.0.0",
20
+ "tailwindcss": "^4.0.0",
21
+ "@tailwindcss/vite": "^4.0.0"
22
+ }
23
+ }`,
24
+ "tsconfig.json": `{
25
+ "compilerOptions": {
26
+ "target": "ES2022",
27
+ "module": "ESNext",
28
+ "moduleResolution": "bundler",
29
+ "jsx": "react-jsx",
30
+ "jsxImportSource": "@emberkit/core",
31
+ "strict": true,
32
+ "esModuleInterop": true,
33
+ "skipLibCheck": true,
34
+ "forceConsistentCasingInFileNames": true,
35
+ "resolveJsonModule": true,
36
+ "isolatedModules": true,
37
+ "noEmit": true,
38
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
39
+ "paths": {
40
+ "@/*": ["./src/*"]
41
+ }
42
+ },
43
+ "include": ["src"],
44
+ "exclude": ["node_modules", "dist"]
45
+ }`,
46
+ "vite.config.ts": `import { defineConfig } from 'vite';
47
+ import { emberkitVitePlugin } from '@emberkit/core/vite-plugin';
48
+ import tailwindcss from '@tailwindcss/vite';
49
+
50
+ export default defineConfig({
51
+ plugins: [emberkitVitePlugin(), tailwindcss()],
52
+ server: {
53
+ port: 3000,
54
+ host: 'localhost',
55
+ },
56
+ esbuild: {
57
+ jsxImportSource: '@emberkit/core',
58
+ },
59
+ });`,
60
+ "index.html": `<!DOCTYPE html>
61
+ <html lang="en">
62
+ <head>
63
+ <meta charset="UTF-8">
64
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
65
+ <title>{{name}}</title>
66
+ <link rel="preconnect" href="https://fonts.googleapis.com">
67
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
68
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
69
+ </head>
70
+ <body id="app">
71
+ <script type="module" src="/src/index.tsx"></script>
72
+ </body>
73
+ </html>`,
74
+ "src/index.tsx": `import { render } from '@emberkit/core';
75
+ import { routes } from 'virtual:emberkit-routes';
76
+ import App from './routes/_layout';
77
+ import './styles.css';
78
+
79
+ const root = document.getElementById('app');
80
+
81
+ if (root) {
82
+ try {
83
+ render(App, root, { routes });
84
+ } catch (error) {
85
+ console.error('[entry] Render error:', error);
86
+ }
87
+ }`,
88
+ "src/styles.css": `@import "tailwindcss";
89
+
90
+ @theme {
91
+ --color-brand-50: #eff6ff;
92
+ --color-brand-100: #dbeafe;
93
+ --color-brand-200: #bfdbfe;
94
+ --color-brand-300: #93c5fd;
95
+ --color-brand-400: #60a5fa;
96
+ --color-brand-500: #3b82f6;
97
+ --color-brand-600: #2563eb;
98
+ --color-brand-700: #1d4ed8;
99
+ --color-brand-800: #1e40af;
100
+ --color-brand-900: #1e3a8a;
101
+ --font-sans: 'Inter', system-ui, sans-serif;
102
+ }
103
+
104
+ body {
105
+ @apply bg-white text-slate-900 font-sans;
106
+ }
107
+
108
+ a {
109
+ @apply text-inherit no-underline;
110
+ }`,
111
+ "src/routes/_layout.tsx": `import type { RouteComponent } from '@emberkit/core';
112
+ import { Head } from '@emberkit/core';
113
+
114
+ const Layout: RouteComponent = ({ children }) => {
115
+ return (
116
+ <>
117
+ <Head>
118
+ <title>{{name}}</title>
119
+ <meta name="description" content="A modern SaaS application built with EmberKit" />
120
+ </Head>
121
+ <div className="min-h-screen flex flex-col">
122
+ <header className="border-b border-slate-200 sticky top-0 bg-white/80 backdrop-blur z-50">
123
+ <div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between">
124
+ <a href="/" className="flex items-center gap-2">
125
+ <span className="text-2xl">&#9889;</span>
126
+ <span className="text-xl font-bold text-brand-600">{{name}}</span>
127
+ </a>
128
+ <nav className="hidden md:flex items-center gap-8 text-sm font-medium text-slate-600">
129
+ <a href="/#features" className="hover:text-brand-600">Features</a>
130
+ <a href="/#pricing" className="hover:text-brand-600">Pricing</a>
131
+ <a href="/about" className="hover:text-brand-600">About</a>
132
+ </nav>
133
+ <div className="flex items-center gap-4">
134
+ <a href="/login" className="text-sm font-medium text-slate-600 hover:text-brand-600">Sign in</a>
135
+ <a href="/signup" className="px-4 py-2 bg-brand-600 hover:bg-brand-700 text-white text-sm font-semibold rounded-lg transition-colors">
136
+ Get Started
137
+ </a>
138
+ </div>
139
+ </div>
140
+ </header>
141
+ <main className="flex-1">{children}</main>
142
+ <footer className="border-t border-slate-200 bg-slate-50 py-12">
143
+ <div className="max-w-7xl mx-auto px-6 grid grid-cols-2 md:grid-cols-4 gap-8">
144
+ <div>
145
+ <h4 className="font-semibold mb-4">Product</h4>
146
+ <ul className="space-y-2 text-sm text-slate-600">
147
+ <li><a href="/#features" className="hover:text-brand-600">Features</a></li>
148
+ <li><a href="/#pricing" className="hover:text-brand-600">Pricing</a></li>
149
+ </ul>
150
+ </div>
151
+ <div>
152
+ <h4 className="font-semibold mb-4">Company</h4>
153
+ <ul className="space-y-2 text-sm text-slate-600">
154
+ <li><a href="/about" className="hover:text-brand-600">About</a></li>
155
+ <li><a href="/blog" className="hover:text-brand-600">Blog</a></li>
156
+ </ul>
157
+ </div>
158
+ <div>
159
+ <h4 className="font-semibold mb-4">Legal</h4>
160
+ <ul className="space-y-2 text-sm text-slate-600">
161
+ <li><a href="/privacy" className="hover:text-brand-600">Privacy</a></li>
162
+ <li><a href="/terms" className="hover:text-brand-600">Terms</a></li>
163
+ </ul>
164
+ </div>
165
+ <div>
166
+ <h4 className="font-semibold mb-4">Support</h4>
167
+ <ul className="space-y-2 text-sm text-slate-600">
168
+ <li><a href="/docs" className="hover:text-brand-600">Docs</a></li>
169
+ <li><a href="/contact" className="hover:text-brand-600">Contact</a></li>
170
+ </ul>
171
+ </div>
172
+ </div>
173
+ <div className="max-w-7xl mx-auto px-6 mt-8 pt-8 border-t border-slate-200 text-center text-sm text-slate-500">
174
+ <p>&copy; 2026 {{name}}. Built with EmberKit.</p>
175
+ </div>
176
+ </footer>
177
+ </div>
178
+ </>
179
+ );
180
+ };
181
+
182
+ export default Layout;`,
183
+ "src/routes/index.tsx": `import type { RouteComponent } from '@emberkit/core';
184
+ import { signal } from '@emberkit/core';
185
+ import { Button, Card, Badge } from '@emberkit/ui';
186
+
187
+ const HomePage: RouteComponent = () => {
188
+ const annual = signal(false);
189
+
190
+ const features = [
191
+ { icon: '&#9889;', title: 'Lightning Fast', desc: 'Sub-10KB runtime with tree-shakeable architecture' },
192
+ { icon: '&#128272;', title: 'Secure by Default', desc: 'Built-in security best practices and protections' },
193
+ { icon: '&#128200;', title: 'Analytics Built-in', desc: 'Track user behavior without third-party scripts' },
194
+ { icon: '&#127758;', title: 'Global CDN', desc: 'Deploy to the edge for minimal latency worldwide' },
195
+ { icon: '&#128268;', title: 'Real-time Sync', desc: 'WebSocket support for live collaboration' },
196
+ { icon: '&#9881;', title: 'Customizable', desc: 'Extend everything with our plugin system' },
197
+ ];
198
+
199
+ const plans = [
200
+ {
201
+ name: 'Starter',
202
+ price: annual.value ? '$0' : '$0',
203
+ period: annual.value ? '/year' : '/month',
204
+ desc: 'Perfect for side projects',
205
+ features: ['1 user', '5 projects', '1GB storage', 'Community support'],
206
+ cta: 'Get Started Free',
207
+ popular: false,
208
+ },
209
+ {
210
+ name: 'Pro',
211
+ price: annual.value ? '$199' : '$19',
212
+ period: annual.value ? '/year' : '/month',
213
+ desc: 'For growing teams',
214
+ features: ['10 users', 'Unlimited projects', '50GB storage', 'Priority support', 'Analytics', 'API access'],
215
+ cta: 'Start Free Trial',
216
+ popular: true,
217
+ },
218
+ {
219
+ name: 'Enterprise',
220
+ price: 'Custom',
221
+ period: '',
222
+ desc: 'For large organizations',
223
+ features: ['Unlimited users', 'Unlimited everything', 'SSO & SAML', 'Dedicated support', 'SLA guarantee', 'Custom integrations'],
224
+ cta: 'Contact Sales',
225
+ popular: false,
226
+ },
227
+ ];
228
+
229
+ return (
230
+ <div>
231
+ {/* Hero */}
232
+ <section className="relative overflow-hidden bg-gradient-to-b from-brand-50 to-white py-24">
233
+ <div className="max-w-5xl mx-auto px-6 text-center">
234
+ <Badge variant="primary" className="mb-6">Now in Public Beta</Badge>
235
+ <h1 className="text-5xl md:text-6xl font-extrabold tracking-tight mb-6">
236
+ Build faster with {' '}
237
+ <span className="bg-gradient-to-r from-brand-500 to-brand-700 bg-clip-text text-transparent">{{name}}</span>
238
+ </h1>
239
+ <p className="text-xl text-slate-600 max-w-2xl mx-auto mb-10">
240
+ The modern SaaS platform that helps teams ship products faster.
241
+ Stop reinventing the wheel and focus on what matters.
242
+ </p>
243
+ <div className="flex gap-4 justify-center">
244
+ <Button variant="primary" size="lg">Start Free Trial</Button>
245
+ <Button variant="secondary" size="lg">Watch Demo</Button>
246
+ </div>
247
+ <p className="mt-4 text-sm text-slate-500">No credit card required &middot; 14-day free trial</p>
248
+ </div>
249
+ </section>
250
+
251
+ {/* Logos */}
252
+ <section className="py-12 border-b border-slate-200">
253
+ <div className="max-w-7xl mx-auto px-6 text-center">
254
+ <p className="text-sm font-medium text-slate-500 mb-8">Trusted by teams at</p>
255
+ <div className="flex justify-center gap-12 opacity-50 grayscale">
256
+ {['Acme', 'Globex', 'Initech', 'Umbrella', 'Stark'].map((name) => (
257
+ <span key={name} className="text-xl font-bold text-slate-400">{name}</span>
258
+ ))}
259
+ </div>
260
+ </div>
261
+ </section>
262
+
263
+ {/* Features */}
264
+ <section id="features" className="py-24">
265
+ <div className="max-w-7xl mx-auto px-6">
266
+ <div className="text-center mb-16">
267
+ <h2 className="text-4xl font-bold mb-4">Everything you need</h2>
268
+ <p className="text-lg text-slate-600 max-w-2xl mx-auto">
269
+ Powerful features to help your team build, ship, and scale.
270
+ </p>
271
+ </div>
272
+ <div className="grid md:grid-cols-3 gap-8">
273
+ {features.map((f) => (
274
+ <Card key={f.title} padding="lg" className="hover:border-brand-300 transition-colors">
275
+ <div className="text-3xl mb-4">{f.icon}</div>
276
+ <h3 className="text-lg font-semibold mb-2">{f.title}</h3>
277
+ <p className="text-slate-600">{f.desc}</p>
278
+ </Card>
279
+ ))}
280
+ </div>
281
+ </div>
282
+ </section>
283
+
284
+ {/* Pricing */}
285
+ <section id="pricing" className="py-24 bg-slate-50">
286
+ <div className="max-w-7xl mx-auto px-6">
287
+ <div className="text-center mb-12">
288
+ <h2 className="text-4xl font-bold mb-4">Simple, transparent pricing</h2>
289
+ <p className="text-lg text-slate-600 mb-8">Choose the plan that works for your team</p>
290
+ <div className="inline-flex items-center gap-3 p-1 bg-white rounded-lg border border-slate-200">
291
+ <button
292
+ className={\`px-4 py-2 text-sm font-medium rounded-md transition-colors \$\{!annual.value ? 'bg-brand-600 text-white' : 'text-slate-600'\}\`}
293
+ onClick={() => { annual.value = false; }}
294
+ >
295
+ Monthly
296
+ </button>
297
+ <button
298
+ className={\`px-4 py-2 text-sm font-medium rounded-md transition-colors \$\{annual.value ? 'bg-brand-600 text-white' : 'text-slate-600'\}\`}
299
+ onClick={() => { annual.value = true; }}
300
+ >
301
+ Annual <span className="text-brand-600 ml-1 font-semibold">-20%</span>
302
+ </button>
303
+ </div>
304
+ </div>
305
+ <div className="grid md:grid-cols-3 gap-8">
306
+ {plans.map((plan) => (
307
+ <Card
308
+ key={plan.name}
309
+ padding="lg"
310
+ className={\`relative \$\{plan.popular ? 'border-brand-500 shadow-lg' : ''\}\`}
311
+ >
312
+ {plan.popular && (
313
+ <Badge variant="primary" className="absolute -top-3 left-6">Most Popular</Badge>
314
+ )}
315
+ <h3 className="text-lg font-semibold mb-2">{plan.name}</h3>
316
+ <p className="text-sm text-slate-600 mb-4">{plan.desc}</p>
317
+ <div className="mb-6">
318
+ <span className="text-4xl font-bold">{plan.price}</span>
319
+ <span className="text-slate-600">{plan.period}</span>
320
+ </div>
321
+ <ul className="space-y-3 mb-8">
322
+ {plan.features.map((f) => (
323
+ <li key={f} className="flex items-center gap-2 text-sm">
324
+ <span className="text-green-500">&#10003;</span>
325
+ {f}
326
+ </li>
327
+ ))}
328
+ </ul>
329
+ <Button
330
+ variant={plan.popular ? 'primary' : 'secondary'}
331
+ className="w-full"
332
+ >
333
+ {plan.cta}
334
+ </Button>
335
+ </Card>
336
+ ))}
337
+ </div>
338
+ </div>
339
+ </section>
340
+
341
+ {/* CTA */}
342
+ <section className="py-24 bg-brand-600 text-white">
343
+ <div className="max-w-3xl mx-auto px-6 text-center">
344
+ <h2 className="text-4xl font-bold mb-4">Ready to get started?</h2>
345
+ <p className="text-lg text-brand-100 mb-8">
346
+ Join thousands of teams already using {{name}}.
347
+ </p>
348
+ <Button variant="secondary" size="lg" className="bg-white text-brand-600 hover:bg-brand-50">
349
+ Start Free Trial
350
+ </Button>
351
+ </div>
352
+ </section>
353
+ </div>
354
+ );
355
+ };
356
+
357
+ export default HomePage;`,
358
+ "src/routes/login.tsx": `import type { RouteComponent } from '@emberkit/core';
359
+ import { Head } from '@emberkit/core';
360
+ import { Button, Input, Card } from '@emberkit/ui';
361
+ import { signal } from '@emberkit/core';
362
+
363
+ const LoginPage: RouteComponent = () => {
364
+ const email = signal('');
365
+ const password = signal('');
366
+ const error = signal<string | null>(null);
367
+
368
+ const handleSubmit = (e: Event) => {
369
+ e.preventDefault();
370
+ error.value = null;
371
+
372
+ if (!email.value || !password.value) {
373
+ error.value = 'Please fill in all fields';
374
+ return;
375
+ }
376
+
377
+ // Handle login
378
+ };
379
+
380
+ return (
381
+ <>
382
+ <Head>
383
+ <title>Sign In - {{name}}</title>
384
+ </Head>
385
+ <div className="min-h-[80vh] flex items-center justify-center px-6">
386
+ <Card padding="xl" className="w-full max-w-md">
387
+ <div className="text-center mb-8">
388
+ <h1 className="text-2xl font-bold mb-2">Welcome back</h1>
389
+ <p className="text-slate-600">Sign in to your account</p>
390
+ </div>
391
+ <form onSubmit={handleSubmit} className="space-y-4">
392
+ <Input
393
+ label="Email"
394
+ type="email"
395
+ placeholder="you@example.com"
396
+ value={email.value}
397
+ onInput={(e) => { email.value = e.currentTarget.value; }}
398
+ />
399
+ <Input
400
+ label="Password"
401
+ type="password"
402
+ placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;"
403
+ value={password.value}
404
+ onInput={(e) => { password.value = e.currentTarget.value; }}
405
+ />
406
+ {error.value && (
407
+ <p className="text-red-500 text-sm">{error.value}</p>
408
+ )}
409
+ <div className="flex items-center justify-between text-sm">
410
+ <label className="flex items-center gap-2">
411
+ <input type="checkbox" className="rounded" />
412
+ Remember me
413
+ </label>
414
+ <a href="/forgot-password" className="text-brand-600 hover:underline">Forgot password?</a>
415
+ </div>
416
+ <Button variant="primary" className="w-full">Sign In</Button>
417
+ </form>
418
+ <p className="text-center text-sm text-slate-600 mt-6">
419
+ Don't have an account? <a href="/signup" className="text-brand-600 font-medium hover:underline">Sign up</a>
420
+ </p>
421
+ </Card>
422
+ </div>
423
+ </>
424
+ );
425
+ };
426
+
427
+ export default LoginPage;`,
428
+ "src/routes/signup.tsx": `import type { RouteComponent } from '@emberkit/core';
429
+ import { Head } from '@emberkit/core';
430
+ import { Button, Input, Card } from '@emberkit/ui';
431
+ import { signal } from '@emberkit/core';
432
+
433
+ const SignupPage: RouteComponent = () => {
434
+ const name = signal('');
435
+ const email = signal('');
436
+ const password = signal('');
437
+ const error = signal<string | null>(null);
438
+
439
+ const handleSubmit = (e: Event) => {
440
+ e.preventDefault();
441
+ error.value = null;
442
+
443
+ if (!name.value || !email.value || !password.value) {
444
+ error.value = 'Please fill in all fields';
445
+ return;
446
+ }
447
+
448
+ // Handle signup
449
+ };
450
+
451
+ return (
452
+ <>
453
+ <Head>
454
+ <title>Sign Up - {{name}}</title>
455
+ </Head>
456
+ <div className="min-h-[80vh] flex items-center justify-center px-6">
457
+ <Card padding="xl" className="w-full max-w-md">
458
+ <div className="text-center mb-8">
459
+ <h1 className="text-2xl font-bold mb-2">Create an account</h1>
460
+ <p className="text-slate-600">Start your 14-day free trial</p>
461
+ </div>
462
+ <form onSubmit={handleSubmit} className="space-y-4">
463
+ <Input
464
+ label="Full Name"
465
+ placeholder="John Doe"
466
+ value={name.value}
467
+ onInput={(e) => { name.value = e.currentTarget.value; }}
468
+ />
469
+ <Input
470
+ label="Email"
471
+ type="email"
472
+ placeholder="you@example.com"
473
+ value={email.value}
474
+ onInput={(e) => { email.value = e.currentTarget.value; }}
475
+ />
476
+ <Input
477
+ label="Password"
478
+ type="password"
479
+ placeholder="8+ characters"
480
+ value={password.value}
481
+ onInput={(e) => { password.value = e.currentTarget.value; }}
482
+ />
483
+ {error.value && (
484
+ <p className="text-red-500 text-sm">{error.value}</p>
485
+ )}
486
+ <p className="text-xs text-slate-500">
487
+ By signing up, you agree to our {' '}
488
+ <a href="/terms" className="text-brand-600 hover:underline">Terms</a> {' '}
489
+ and {' '}
490
+ <a href="/privacy" className="text-brand-600 hover:underline">Privacy Policy</a>
491
+ </p>
492
+ <Button variant="primary" className="w-full">Create Account</Button>
493
+ </form>
494
+ <p className="text-center text-sm text-slate-600 mt-6">
495
+ Already have an account? <a href="/login" className="text-brand-600 font-medium hover:underline">Sign in</a>
496
+ </p>
497
+ </Card>
498
+ </div>
499
+ </>
500
+ );
501
+ };
502
+
503
+ export default SignupPage;`,
504
+ "src/routes/about.tsx": `import type { RouteComponent } from '@emberkit/core';
505
+ import { Head } from '@emberkit/core';
506
+
507
+ const AboutPage: RouteComponent = () => {
508
+ return (
509
+ <>
510
+ <Head>
511
+ <title>About - {{name}}</title>
512
+ </Head>
513
+ <div className="max-w-3xl mx-auto px-6 py-16">
514
+ <h1 className="text-4xl font-bold mb-6">About {{name}}</h1>
515
+ <div className="prose prose-slate">
516
+ <p class="text-lg text-slate-600 mb-6">
517
+ We're on a mission to make software development faster and more enjoyable.
518
+ Our platform provides everything you need to build, deploy, and scale modern web applications.
519
+ </p>
520
+ <h2 class="text-2xl font-bold mt-8 mb-4">Our Story</h2>
521
+ <p class="text-slate-600 mb-4">
522
+ Founded in 2026, {{name}} was born from the frustration of dealing with complex,
523
+ bloated frameworks. We believe in simplicity, performance, and developer experience.
524
+ </p>
525
+ <h2 class="text-2xl font-bold mt-8 mb-4">Values</h2>
526
+ <ul class="list-disc pl-6 space-y-2 text-slate-600">
527
+ <li>Developer experience first</li>
528
+ <li>Performance is a feature</li>
529
+ <li>Simplicity over complexity</li>
530
+ <li>Open and transparent</li>
531
+ </ul>
532
+ </div>
533
+ </div>
534
+ </>
535
+ );
536
+ };
537
+
538
+ export default AboutPage;`,
539
+ };
@@ -0,0 +1,25 @@
1
+ export const signalTemplate = `import { signal, computed, effect } from '@emberkit/core';
2
+
3
+ // Writable signal
4
+ const count = signal(0);
5
+
6
+ // Computed value
7
+ const doubled = computed(() => count.value * 2);
8
+
9
+ // Side effect
10
+ effect(() => {
11
+ console.log('Count changed to:', count.value);
12
+ });
13
+
14
+ // Update
15
+ count.value++;
16
+
17
+ // Batch updates
18
+ import { batch } from '@emberkit/core';
19
+
20
+ batch(() => {
21
+ count.value = 10;
22
+ });
23
+
24
+ export { count, doubled };
25
+ `;
@@ -1,26 +1,26 @@
1
+ import { existsSync, mkdirSync, readFileSync as fsReadFileSync, writeFileSync as fsWriteFileSync } from "fs";
2
+ import { resolve, dirname } from "path";
3
+ import { execSync } from "child_process";
4
+ import { platform } from "os";
1
5
  export { generate, toPascalCase, toKebabCase } from "./generator.js";
2
6
  export function ensureDirSync(dirPath) {
3
- const { mkdirSync, existsSync } = require("fs");
4
7
  if (!existsSync(dirPath)) {
5
8
  mkdirSync(dirPath, { recursive: true });
6
9
  }
7
10
  }
8
11
  export function readFileSync(filePath) {
9
- const { readFileSync } = require("fs");
10
- return readFileSync(filePath, "utf-8");
12
+ return fsReadFileSync(filePath, "utf-8");
11
13
  }
12
14
  export function writeFileSync(filePath, content, options) {
13
- const fs = require("fs");
14
- const dir = require("path").dirname(filePath);
15
+ const dir = dirname(filePath);
15
16
  ensureDirSync(dir);
16
- fs.writeFileSync(filePath, content, options ?? { encoding: "utf-8" });
17
+ fsWriteFileSync(filePath, content, options ?? { encoding: "utf-8" });
17
18
  }
18
19
  export function fileExists(filePath) {
19
- const { existsSync } = require("fs");
20
20
  return existsSync(filePath);
21
21
  }
22
22
  export function resolvePath(...segments) {
23
- return require("path").resolve(...segments);
23
+ return resolve(...segments);
24
24
  }
25
25
  export function getPackageManager() {
26
26
  const userAgent = process.env.npm_config_user_agent ?? "";
@@ -30,16 +30,14 @@ export function getPackageManager() {
30
30
  return "yarn";
31
31
  if (userAgent.startsWith("npm"))
32
32
  return "npm";
33
- const { existsSync } = require("fs");
34
- const { platform } = require("os");
35
33
  try {
36
34
  if (platform() === "win32") {
37
- if (existsSync("C:\\Program Files\\pnpm\\pnpm.exe") || existsSync(process.env.LOCALAPPDATA + "\\pnpm\\pnpm.exe")) {
35
+ const localAppData = process.env.LOCALAPPDATA ?? "";
36
+ if (existsSync("C:\\Program Files\\pnpm\\pnpm.exe") || existsSync(localAppData + "\\pnpm\\pnpm.exe")) {
38
37
  return "pnpm";
39
38
  }
40
39
  }
41
40
  else {
42
- const { execSync } = require("child_process");
43
41
  execSync("pnpm --version", { stdio: "ignore" });
44
42
  return "pnpm";
45
43
  }
@@ -1,6 +1,19 @@
1
1
  import { existsSync, mkdirSync, writeFileSync } from "fs";
2
2
  import { dirname, join } from "path";
3
- import { getTemplate, formatTemplate, toKebabCase, } from "../templates/index.js";
3
+ import { formatTemplate, toKebabCase } from "../templates/index.js";
4
+ import { routeTemplate } from "../templates/route.js";
5
+ import { componentTemplate } from "../templates/component.js";
6
+ import { layoutTemplate } from "../templates/layout.js";
7
+ import { errorBoundaryTemplate } from "../templates/errorBoundary.js";
8
+ import { loaderTemplate } from "../templates/loader.js";
9
+ import { actionTemplate } from "../templates/action.js";
10
+ import { apiRouteTemplate } from "../templates/apiRoute.js";
11
+ import { configTemplate } from "../templates/config.js";
12
+ import { indexTemplate } from "../templates/entry.js";
13
+ import { layoutRoutesTemplate } from "../templates/layoutRoutes.js";
14
+ import { signalTemplate } from "../templates/signal.js";
15
+ import { contextTemplate } from "../templates/context.js";
16
+ import { formTemplate } from "../templates/form.js";
4
17
  export async function generate(options) {
5
18
  const { name, path, template, params = {} } = options;
6
19
  const fullPath = join(process.cwd(), path);
@@ -9,7 +22,22 @@ export async function generate(options) {
9
22
  kebabName: toKebabCase(name),
10
23
  ...params,
11
24
  };
12
- const templateContent = getTemplate(template);
25
+ const templates = {
26
+ route: routeTemplate,
27
+ component: componentTemplate,
28
+ layout: layoutTemplate,
29
+ error: errorBoundaryTemplate,
30
+ loader: loaderTemplate,
31
+ action: actionTemplate,
32
+ api: apiRouteTemplate,
33
+ config: configTemplate,
34
+ index: indexTemplate,
35
+ layoutRoutes: layoutRoutesTemplate,
36
+ signal: signalTemplate,
37
+ context: contextTemplate,
38
+ form: formTemplate,
39
+ };
40
+ const templateContent = templates[template] ?? routeTemplate;
13
41
  const content = formatTemplate(templateContent, formattedParams);
14
42
  try {
15
43
  const dir = dirname(fullPath);