@igniter-js/cli 0.1.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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +358 -0
- package/dist/templates/components.json.hbs +21 -0
- package/dist/templates/copilot.instructions.hbs +117 -0
- package/dist/templates/docker-compose.hbs +15 -0
- package/dist/templates/env.hbs +33 -0
- package/dist/templates/eslintrc.hbs +3 -0
- package/dist/templates/feature.controller.hbs +83 -0
- package/dist/templates/feature.index.hbs +5 -0
- package/dist/templates/feature.interface.hbs +71 -0
- package/dist/templates/feature.procedure.hbs +76 -0
- package/dist/templates/globals.hbs +198 -0
- package/dist/templates/igniter.client.hbs +21 -0
- package/dist/templates/igniter.context.hbs +23 -0
- package/dist/templates/igniter.hbs +8 -0
- package/dist/templates/igniter.router.hbs +29 -0
- package/dist/templates/layout.hbs +54 -0
- package/dist/templates/page.hbs +313 -0
- package/dist/templates/prisma.hbs +9 -0
- package/dist/templates/readme.hbs +136 -0
- package/dist/templates/vitest.config.hbs +11 -0
- package/dist/utils/analyze.d.ts +17 -0
- package/dist/utils/analyze.js +187 -0
- package/dist/utils/consts.d.ts +26 -0
- package/dist/utils/consts.js +34 -0
- package/dist/utils/handlebars-helpers.d.ts +1 -0
- package/dist/utils/handlebars-helpers.js +43 -0
- package/dist/utils/helpers.d.ts +12 -0
- package/dist/utils/helpers.js +102 -0
- package/dist/utils/prisma-schema-parser.d.ts +59 -0
- package/dist/utils/prisma-schema-parser.js +197 -0
- package/dist/utils/template-handler.d.ts +6 -0
- package/dist/utils/template-handler.js +33 -0
- package/package.json +73 -0
- package/readme.md +143 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { Badge } from "@/core/design-system/badge"
|
|
2
|
+
import { Button } from "@/core/design-system/button"
|
|
3
|
+
import { Card, CardContent, CardFooter } from "@/core/design-system/card"
|
|
4
|
+
import { ArrowRight, Code2, Github, SparkleIcon, Twitter, Youtube } from "lucide-react"
|
|
5
|
+
import { FileText } from "lucide-react"
|
|
6
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@radix-ui/react-tabs"
|
|
7
|
+
|
|
8
|
+
export default async function Home() {
|
|
9
|
+
return (
|
|
10
|
+
<div className="min-h-screen flex flex-col pt-12 space-y-12">
|
|
11
|
+
<header className="max-w-2xl w-full space-y-8 mx-auto my-auto">
|
|
12
|
+
<div className="space-y-6 text-center">
|
|
13
|
+
<Badge variant="outline" className="mx-auto rounded-full px-3 py-1 text-sm font-medium bg-cyan-500/10 text-cyan-500 border-cyan-300/10/20" aria-label="Beta version">
|
|
14
|
+
BETA
|
|
15
|
+
</Badge>
|
|
16
|
+
|
|
17
|
+
<h1 className="text-4xl sm:text-5xl font-bold tracking-tight bg-gradient-to-r from-primary to-primary/60 bg-clip-text text-transparent">
|
|
18
|
+
Welcome to <br />Igniter Framework
|
|
19
|
+
</h1>
|
|
20
|
+
<p className="text-base sm:text-lg text-muted-foreground max-w-md mx-auto leading-relaxed">
|
|
21
|
+
A modern framework optimized for AI-powered development, featuring built-in best practices and type safety for rapid web applications
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<div className="flex items-center justify-center gap-4">
|
|
25
|
+
<Button variant="outline" size="icon" asChild>
|
|
26
|
+
<a href="https://github.com/feldbarcelospro/igniter-js" target="_blank" rel="noopener noreferrer">
|
|
27
|
+
<Github className="h-4 w-4" />
|
|
28
|
+
<span className="sr-only">GitHub Repository</span>
|
|
29
|
+
</a>
|
|
30
|
+
</Button>
|
|
31
|
+
<Button variant="outline" size="icon" asChild>
|
|
32
|
+
<a href="https://twitter.com/feldbarcelospro" target="_blank" rel="noopener noreferrer">
|
|
33
|
+
<Twitter className="h-4 w-4" />
|
|
34
|
+
<span className="sr-only">X (Twitter) Profile</span>
|
|
35
|
+
</a>
|
|
36
|
+
</Button>
|
|
37
|
+
<Button variant="outline" size="icon" asChild>
|
|
38
|
+
<a href="https://youtube.com/@feldbarcelospro" target="_blank" rel="noopener noreferrer">
|
|
39
|
+
<Youtube className="h-4 w-4" />
|
|
40
|
+
<span className="sr-only">YouTube Channel</span>
|
|
41
|
+
</a>
|
|
42
|
+
</Button>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
<section aria-labelledby="getting-started-title" className="relative">
|
|
46
|
+
<div className="absolute inset-0 bg-gradient-to-b from-transparent to-background/10 pointer-events-none" aria-hidden="true" />
|
|
47
|
+
<Card className="rounded-3xl shadow-lg backdrop-blur-sm border bg-card/50 overflow-hidden">
|
|
48
|
+
<CardContent className="p-6 sm:p-8">
|
|
49
|
+
<Tabs defaultValue="getting-started" className="space-y-6 flex flex-col items-center">
|
|
50
|
+
<TabsList className="inline-flex h-12 items-center justify-center rounded-full bg-muted p-1 text-muted-foreground">
|
|
51
|
+
<TabsTrigger
|
|
52
|
+
value="getting-started"
|
|
53
|
+
className="inline-flex w-fit items-center justify-center whitespace-nowrap rounded-full px-6 py-2.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm"
|
|
54
|
+
>
|
|
55
|
+
<Code2 className="mr-2 h-4 w-4" />
|
|
56
|
+
Getting Started
|
|
57
|
+
</TabsTrigger>
|
|
58
|
+
<TabsTrigger
|
|
59
|
+
value="first-feature"
|
|
60
|
+
className="inline-flex items-center justify-center whitespace-nowrap rounded-full px-6 py-2.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm"
|
|
61
|
+
>
|
|
62
|
+
<FileText className="mr-2 h-4 w-4" />
|
|
63
|
+
First Feature
|
|
64
|
+
</TabsTrigger>
|
|
65
|
+
<TabsTrigger
|
|
66
|
+
value="extensions"
|
|
67
|
+
className="inline-flex items-center justify-center whitespace-nowrap rounded-full px-6 py-2.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm"
|
|
68
|
+
>
|
|
69
|
+
<SparkleIcon className="mr-2 h-4 w-4" />
|
|
70
|
+
Add your first Spark (Comming Soon)
|
|
71
|
+
</TabsTrigger>
|
|
72
|
+
</TabsList>
|
|
73
|
+
|
|
74
|
+
<TabsContent value="getting-started" className="space-y-6 w-full">
|
|
75
|
+
<div className="space-y-2 text-center flex flex-col items-center justify-center">
|
|
76
|
+
<h2 className="text-2xl font-bold tracking-tight">Getting Started</h2>
|
|
77
|
+
<p className="text-muted-foreground">Follow these steps to set up your development environment.</p>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div className="space-y-4">
|
|
81
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
82
|
+
<p className="font-medium text-foreground mb-2">1. Install dependencies</p>
|
|
83
|
+
<code className="relative rounded-xl bg-muted border px-4 py-3 font-mono text-sm block group-hover:border-accent transition-colors">
|
|
84
|
+
npm install
|
|
85
|
+
</code>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
89
|
+
<p className="font-medium text-foreground mb-2">2. Start development server</p>
|
|
90
|
+
<code className="relative rounded-xl bg-muted border px-4 py-3 font-mono text-sm block group-hover:border-accent transition-colors">
|
|
91
|
+
npm run dev
|
|
92
|
+
</code>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
96
|
+
<p className="font-medium text-foreground mb-2">3. View your application</p>
|
|
97
|
+
<div className="flex items-center gap-2">
|
|
98
|
+
<span className="h-2 w-2 rounded-full bg-green-500 animate-pulse"></span>
|
|
99
|
+
<code className="text-primary">http://localhost:3000</code>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</TabsContent>
|
|
104
|
+
|
|
105
|
+
<TabsContent value="first-feature" className="space-y-6 w-full">
|
|
106
|
+
<div className="space-y-2 text-center flex flex-col items-center justify-center">
|
|
107
|
+
<h2 className="text-2xl font-bold tracking-tight">Create Your First Feature</h2>
|
|
108
|
+
<p className="text-muted-foreground">Follow these steps to create and configure a new feature.</p>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div className="space-y-4">
|
|
112
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
113
|
+
<p className="font-medium text-foreground mb-2">1. Add your entity on schema.prisma</p>
|
|
114
|
+
<code className="relative rounded-xl bg-muted border px-4 py-3 font-mono text-sm block group-hover:border-accent transition-colors">
|
|
115
|
+
{`// Example:
|
|
116
|
+
model Post {
|
|
117
|
+
id Int @id @default(autoincrement())
|
|
118
|
+
title String
|
|
119
|
+
content String?
|
|
120
|
+
createdAt DateTime @default(now())
|
|
121
|
+
updatedAt DateTime @updatedAt
|
|
122
|
+
}`}
|
|
123
|
+
</code>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
127
|
+
<p className="font-medium text-foreground mb-2">2. Run CLI and select features to generate</p>
|
|
128
|
+
<code className="relative rounded-xl bg-muted border px-4 py-3 font-mono text-sm block group-hover:border-accent transition-colors">
|
|
129
|
+
igniter generate feature
|
|
130
|
+
</code>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
134
|
+
<p className="font-medium text-foreground mb-2">4. Add to /src/igniter.router.ts</p>
|
|
135
|
+
<code className="relative rounded-xl bg-muted border px-4 py-3 font-mono text-sm block group-hover:border-accent transition-colors">
|
|
136
|
+
{`// Example:
|
|
137
|
+
import { PostFeature } from '@/features/post'
|
|
138
|
+
|
|
139
|
+
export const router = new IgniterRouter()
|
|
140
|
+
.use(postFeature.controller)
|
|
141
|
+
`}
|
|
142
|
+
</code>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
146
|
+
<p className="font-medium text-foreground mb-2">5. Use in your application</p>
|
|
147
|
+
<p className="text-sm text-muted-foreground">
|
|
148
|
+
{`// Server Component Example:
|
|
149
|
+
import { PostFeature } from '@/features/post'
|
|
150
|
+
|
|
151
|
+
export default async function Page() {
|
|
152
|
+
const getPosts = router.bind('post', 'get')
|
|
153
|
+
const posts = await getPosts()
|
|
154
|
+
|
|
155
|
+
return <pre>{JSON.stringify(posts, null, 2)}</pre>
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Client Component Example:
|
|
159
|
+
'use client'
|
|
160
|
+
|
|
161
|
+
import { PostFeature } from '@/features/post'
|
|
162
|
+
import { useAction } from '@igniter/hooks'
|
|
163
|
+
|
|
164
|
+
export default function Page() {
|
|
165
|
+
const { data, execute } = useAction({
|
|
166
|
+
action: router.bind('post', 'get')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<>
|
|
171
|
+
<button onClick={execute}>Load Posts</button>
|
|
172
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
173
|
+
</>
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
`}
|
|
177
|
+
</p >
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</TabsContent>
|
|
181
|
+
|
|
182
|
+
<TabsContent value="extensions" className="space-y-6 w-full">
|
|
183
|
+
<div className="space-y-2 text-center flex flex-col items-center justify-center">
|
|
184
|
+
<h2 className="text-2xl font-bold tracking-tight">Ignite Sparks</h2>
|
|
185
|
+
<p className="text-muted-foreground">Add powerful features that ignite your application.</p>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div className="space-y-4">
|
|
189
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
190
|
+
<p className="font-medium text-foreground mb-2">1. Discover available Sparks</p>
|
|
191
|
+
<code className="relative rounded-xl bg-muted border px-4 py-3 font-mono text-sm block group-hover:border-accent transition-colors">
|
|
192
|
+
npm run igniter spark:list
|
|
193
|
+
</code>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
197
|
+
<p className="font-medium text-foreground mb-2">2. Ignite a single Spark</p>
|
|
198
|
+
<code className="relative rounded-xl bg-muted border px-4 py-3 font-mono text-sm block group-hover:border-accent transition-colors">
|
|
199
|
+
npm run igniter spark:add auth
|
|
200
|
+
</code>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<div className="group rounded-2xl border p-4 transition-colors hover:bg-accent">
|
|
204
|
+
<p className="font-medium text-foreground mb-2">3. Ignite multiple Sparks</p>
|
|
205
|
+
<code className="relative rounded-xl bg-muted border px-4 py-3 font-mono text-sm block group-hover:border-accent transition-colors">
|
|
206
|
+
npm run igniter spark:add email storage payment
|
|
207
|
+
</code>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</TabsContent>
|
|
211
|
+
</Tabs>
|
|
212
|
+
</CardContent>
|
|
213
|
+
<CardFooter className="flex flex-col items-center justify-center border-t pt-8">
|
|
214
|
+
<Button variant="outline" size="lg" className="w-full rounded-xl">
|
|
215
|
+
Read Docs
|
|
216
|
+
<ArrowRight className="w-4 h-4 ml-auto" aria-hidden="true" />
|
|
217
|
+
</Button>
|
|
218
|
+
</CardFooter>
|
|
219
|
+
</Card>
|
|
220
|
+
</section>
|
|
221
|
+
|
|
222
|
+
<section className="grid gap-4 sm:grid-cols-2" aria-label="Resources">
|
|
223
|
+
<article>
|
|
224
|
+
<Card className="rounded-2xl shadow-sm hover:shadow-md transition-shadow bg-card">
|
|
225
|
+
<CardContent className="p-4 sm:p-6 space-y-2">
|
|
226
|
+
<span className="h-10 w-10 rounded-lg border border-cyan-300/10 bg-cyan-500/10 flex items-center justify-center mb-4">
|
|
227
|
+
<FileText className="w-5 h-5 text-cyan-500" aria-hidden="true" />
|
|
228
|
+
</span>
|
|
229
|
+
<div className="flex items-center gap-2">
|
|
230
|
+
<h3 className="font-semibold">Documentation</h3>
|
|
231
|
+
</div>
|
|
232
|
+
<p className="text-sm text-muted-foreground">
|
|
233
|
+
Learn more about Igniter Framework features and API.
|
|
234
|
+
</p>
|
|
235
|
+
<Button variant="outline" className="w-full !mt-8 rounded-xl">
|
|
236
|
+
Read Docs
|
|
237
|
+
<ArrowRight className="w-4 h-4 ml-auto" aria-hidden="true" />
|
|
238
|
+
</Button>
|
|
239
|
+
</CardContent>
|
|
240
|
+
</Card>
|
|
241
|
+
</article>
|
|
242
|
+
|
|
243
|
+
<article>
|
|
244
|
+
<Card className="rounded-2xl shadow-sm hover:shadow-md transition-shadow bg-card">
|
|
245
|
+
<CardContent className="p-4 sm:p-6 space-y-2">
|
|
246
|
+
<span className="h-10 w-10 rounded-lg border border-cyan-300/10 bg-cyan-500/10 flex items-center justify-center mb-4">
|
|
247
|
+
<Code2 className="w-5 h-5 text-cyan-500" aria-hidden="true" />
|
|
248
|
+
</span>
|
|
249
|
+
<div className="flex items-center gap-2">
|
|
250
|
+
<h3 className="font-semibold">Examples</h3>
|
|
251
|
+
</div>
|
|
252
|
+
<p className="text-sm text-muted-foreground">
|
|
253
|
+
Discover and deploy example Igniter Framework projects.
|
|
254
|
+
</p>
|
|
255
|
+
<Button variant="outline" className="w-full !mt-8 rounded-xl">
|
|
256
|
+
View Examples
|
|
257
|
+
<ArrowRight className="w-4 h-4 ml-auto" aria-hidden="true" />
|
|
258
|
+
</Button>
|
|
259
|
+
</CardContent>
|
|
260
|
+
</Card>
|
|
261
|
+
</article>
|
|
262
|
+
</section>
|
|
263
|
+
</header>
|
|
264
|
+
|
|
265
|
+
<footer className="border-t pt-6 text-center w-full text-sm text-muted-foreground h-16">
|
|
266
|
+
<nav aria-label="Footer Links">
|
|
267
|
+
<p>
|
|
268
|
+
Built with{" "}
|
|
269
|
+
<a
|
|
270
|
+
href="https://ui.shadcn.com"
|
|
271
|
+
target="_blank"
|
|
272
|
+
rel="noopener noreferrer"
|
|
273
|
+
className="font-medium underline underline-offset-4 hover:text-primary transition-colors"
|
|
274
|
+
aria-label="Visit shadcn/ui website"
|
|
275
|
+
>
|
|
276
|
+
shadcn/ui
|
|
277
|
+
</a>
|
|
278
|
+
{", "}
|
|
279
|
+
<a
|
|
280
|
+
href="https://nextjs.org"
|
|
281
|
+
target="_blank"
|
|
282
|
+
rel="noopener noreferrer"
|
|
283
|
+
className="font-medium underline underline-offset-4 hover:text-primary transition-colors"
|
|
284
|
+
aria-label="Visit Next.js website"
|
|
285
|
+
>
|
|
286
|
+
Next.js 15
|
|
287
|
+
</a>
|
|
288
|
+
{", "}
|
|
289
|
+
<a
|
|
290
|
+
href="https://www.prisma.io"
|
|
291
|
+
target="_blank"
|
|
292
|
+
rel="noopener noreferrer"
|
|
293
|
+
className="font-medium underline underline-offset-4 hover:text-primary transition-colors"
|
|
294
|
+
aria-label="Visit Prisma website"
|
|
295
|
+
>
|
|
296
|
+
Prisma
|
|
297
|
+
</a>
|
|
298
|
+
{", and "}
|
|
299
|
+
<a
|
|
300
|
+
href="https://zod.dev"
|
|
301
|
+
target="_blank"
|
|
302
|
+
rel="noopener noreferrer"
|
|
303
|
+
className="font-medium underline underline-offset-4 hover:text-primary transition-colors"
|
|
304
|
+
aria-label="Visit Zod website"
|
|
305
|
+
>
|
|
306
|
+
Zod
|
|
307
|
+
</a>
|
|
308
|
+
</p>
|
|
309
|
+
</nav>
|
|
310
|
+
</footer>
|
|
311
|
+
</div>
|
|
312
|
+
)
|
|
313
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client'
|
|
2
|
+
|
|
3
|
+
const globalForPrisma = globalThis as unknown as {
|
|
4
|
+
prisma: PrismaClient | undefined
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
|
|
8
|
+
|
|
9
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
A modern web application built with [Igniter Framework](https://github.com/felipebarcelospro/igniter-js) - A feature-first framework for Next.js projects.
|
|
4
|
+
|
|
5
|
+
## 🚀 Quick Start
|
|
6
|
+
|
|
7
|
+
1. **Install Dependencies**
|
|
8
|
+
```bash
|
|
9
|
+
npm install
|
|
10
|
+
# or
|
|
11
|
+
yarn install
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
2. **Setup Database**
|
|
15
|
+
```bash
|
|
16
|
+
# Start Docker containers
|
|
17
|
+
npm run docker:up
|
|
18
|
+
|
|
19
|
+
# Generate Prisma Client
|
|
20
|
+
npm run db:generate
|
|
21
|
+
|
|
22
|
+
# Push database schema
|
|
23
|
+
npm run db:push
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
3. **Start Development Server**
|
|
27
|
+
```bash
|
|
28
|
+
npm run dev
|
|
29
|
+
# or
|
|
30
|
+
yarn dev
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Open [http://localhost:3000](http://localhost:3000) to view your application.
|
|
34
|
+
|
|
35
|
+
## 📖 Project Structure
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
├── src/
|
|
39
|
+
│ ├── app/ # Next.js app directory
|
|
40
|
+
│ ├── core/ # Core framework modules
|
|
41
|
+
│ │ ├── design-system/ # UI components (shadcn/ui)
|
|
42
|
+
│ │ ├── factories/ # Base factories for features
|
|
43
|
+
│ │ ├── providers/ # Context providers
|
|
44
|
+
│ │ └── utils/ # Shared utilities
|
|
45
|
+
│ ├── features/ # Feature modules
|
|
46
|
+
│ │ └── [feature]/ # Feature-specific code
|
|
47
|
+
│ │ ├── index.ts # Feature entry point
|
|
48
|
+
│ │ ├── controllers/ # HTTP request handlers
|
|
49
|
+
│ │ │ └── [feature].controller.ts
|
|
50
|
+
│ │ ├── services/ # Business logic
|
|
51
|
+
│ │ │ └── [feature].service.ts
|
|
52
|
+
│ │ ├── repositories/ # Data access
|
|
53
|
+
│ │ │ └── [feature].repository.ts
|
|
54
|
+
│ │ ├── validators/ # Data validation schemas
|
|
55
|
+
│ │ │ └── [feature].validator.ts
|
|
56
|
+
│ │ ├── [feature].types.ts # Feature types
|
|
57
|
+
│ │ └── [feature].feature.ts # Feature configuration
|
|
58
|
+
│ └── configs/ # Configuration files
|
|
59
|
+
├── public/ # Static files
|
|
60
|
+
├── docs/ # Documentation
|
|
61
|
+
├── scripts/ # Utility scripts
|
|
62
|
+
└── .github/ # GitHub configuration
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 🛠 Available Scripts
|
|
66
|
+
|
|
67
|
+
- `npm run dev` - Start development server
|
|
68
|
+
- `npm run build` - Build for production
|
|
69
|
+
- `npm run start` - Start production server
|
|
70
|
+
- `npm run lint` - Run ESLint
|
|
71
|
+
- `npm run test` - Run tests with Vitest
|
|
72
|
+
|
|
73
|
+
### Database Scripts
|
|
74
|
+
- `npm run docker:up` - Start Docker containers
|
|
75
|
+
- `npm run docker:down` - Stop Docker containers
|
|
76
|
+
- `npm run db:studio` - Open Prisma Studio
|
|
77
|
+
- `npm run db:push` - Push database schema changes
|
|
78
|
+
- `npm run db:generate` - Generate Prisma Client
|
|
79
|
+
|
|
80
|
+
### Igniter CLI Commands
|
|
81
|
+
- `npm run igniter:generate feature [name]` - Generate a new feature
|
|
82
|
+
|
|
83
|
+
## 🏗 Feature Generation
|
|
84
|
+
Generate a new feature using the Igniter CLI:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm run igniter generate feature
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This will create a new feature with the following structure:
|
|
91
|
+
```
|
|
92
|
+
src/features/users/
|
|
93
|
+
├── index.ts # Feature exports
|
|
94
|
+
├── controller.ts # HTTP request handling
|
|
95
|
+
├── service.ts # Business logic
|
|
96
|
+
├── repository.ts # Data access
|
|
97
|
+
├── factory.ts # Object creation
|
|
98
|
+
└── schema.ts # Data validation
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 🧩 Tech Stack
|
|
102
|
+
|
|
103
|
+
- **Framework**: Igniter.js (Next.js 15)
|
|
104
|
+
- **Language**: TypeScript
|
|
105
|
+
- **Styling**: Tailwind CSS
|
|
106
|
+
- **UI Components**: shadcn/ui
|
|
107
|
+
- **Database**: Prisma
|
|
108
|
+
- **Testing**: Vitest
|
|
109
|
+
- **State Management**: React Context
|
|
110
|
+
- **API Layer**: Feature-based controllers
|
|
111
|
+
- **Validation**: Zod
|
|
112
|
+
- **Development**: Docker
|
|
113
|
+
|
|
114
|
+
## 📚 Documentation
|
|
115
|
+
|
|
116
|
+
For more detailed documentation:
|
|
117
|
+
|
|
118
|
+
- [Igniter Framework Documentation](https://github.com/felipebarcelospro/igniter-js)
|
|
119
|
+
- [Next.js Documentation](https://nextjs.org/docs)
|
|
120
|
+
- [Prisma Documentation](https://www.prisma.io/docs)
|
|
121
|
+
- [shadcn/ui Documentation](https://ui.shadcn.com)
|
|
122
|
+
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
|
|
123
|
+
- [Vitest Documentation](https://vitest.dev/docs)
|
|
124
|
+
- [Zod Documentation](https://zod.dev)
|
|
125
|
+
|
|
126
|
+
## 🤝 Contributing
|
|
127
|
+
|
|
128
|
+
1. Fork the repository
|
|
129
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
130
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
131
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
132
|
+
5. Open a Pull Request
|
|
133
|
+
|
|
134
|
+
## 📝 License
|
|
135
|
+
|
|
136
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="vitest" />
|
|
2
|
+
import { defineConfig } from 'vitest/config'
|
|
3
|
+
import react from '@vitejs/plugin-react'
|
|
4
|
+
import tsconfigPaths from 'vite-tsconfig-paths'
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [tsconfigPaths(), react()],
|
|
8
|
+
test: {
|
|
9
|
+
environment: 'jsdom',
|
|
10
|
+
},
|
|
11
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CLIHelper } from './helpers';
|
|
2
|
+
export declare class AnalyzeCommand extends CLIHelper {
|
|
3
|
+
private readonly schemaParser;
|
|
4
|
+
private report;
|
|
5
|
+
private spinner;
|
|
6
|
+
constructor(basePath?: string);
|
|
7
|
+
private initializeReport;
|
|
8
|
+
analyze(): Promise<void>;
|
|
9
|
+
private analyzeSchema;
|
|
10
|
+
private analyzeFeatures;
|
|
11
|
+
private analyzeDependencies;
|
|
12
|
+
private analyzePerformance;
|
|
13
|
+
private getAllFiles;
|
|
14
|
+
private countApiEndpoints;
|
|
15
|
+
private calculateBundleSize;
|
|
16
|
+
private displayReport;
|
|
17
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AnalyzeCommand = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const util_1 = require("util");
|
|
12
|
+
const prisma_schema_parser_1 = require("../utils/prisma-schema-parser");
|
|
13
|
+
const helpers_1 = require("./helpers");
|
|
14
|
+
const ora_1 = __importDefault(require("ora"));
|
|
15
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
16
|
+
class AnalyzeCommand extends helpers_1.CLIHelper {
|
|
17
|
+
constructor(basePath = process.cwd()) {
|
|
18
|
+
super();
|
|
19
|
+
this.schemaParser = new prisma_schema_parser_1.PrismaSchemaParser(basePath);
|
|
20
|
+
this.report = this.initializeReport();
|
|
21
|
+
this.spinner = (0, ora_1.default)();
|
|
22
|
+
}
|
|
23
|
+
initializeReport() {
|
|
24
|
+
return {
|
|
25
|
+
schema: { models: 0, relations: 0, complexModels: [] },
|
|
26
|
+
features: { count: 0, unused: [], incomplete: [], complexity: {} },
|
|
27
|
+
dependencies: { unused: [], outdated: [], security: [] },
|
|
28
|
+
performance: { bundleSize: '0KB', apiEndpoints: 0, slowQueries: [] }
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async analyze() {
|
|
32
|
+
console.clear();
|
|
33
|
+
this.spinner.start('Analyzing project structure');
|
|
34
|
+
try {
|
|
35
|
+
await this.analyzeSchema();
|
|
36
|
+
await this.analyzeFeatures();
|
|
37
|
+
await this.analyzeDependencies();
|
|
38
|
+
await this.analyzePerformance();
|
|
39
|
+
this.spinner.succeed('Analysis completed');
|
|
40
|
+
this.displayReport();
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
this.spinner.fail('Analysis failed');
|
|
44
|
+
console.error(chalk_1.default.red(error));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async analyzeSchema() {
|
|
49
|
+
var _a;
|
|
50
|
+
this.spinner.start('Analyzing Prisma schema');
|
|
51
|
+
const content = this.schemaParser.getSchemaContent();
|
|
52
|
+
const modelMatches = content.match(/model\s+\w+\s*{[^}]*}/g) || [];
|
|
53
|
+
this.report.schema.models = modelMatches.length;
|
|
54
|
+
// Analyze relations and complex models
|
|
55
|
+
for (const model of modelMatches) {
|
|
56
|
+
const relations = (model.match(/@relation/g) || []).length;
|
|
57
|
+
this.report.schema.relations += relations;
|
|
58
|
+
const modelName = ((_a = model.match(/model\s+(\w+)/)) === null || _a === void 0 ? void 0 : _a[1]) || '';
|
|
59
|
+
if (relations > 3) {
|
|
60
|
+
this.report.schema.complexModels.push(modelName);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
this.spinner.succeed('Schema analysis completed');
|
|
64
|
+
}
|
|
65
|
+
async analyzeFeatures() {
|
|
66
|
+
this.spinner.start('Analyzing features');
|
|
67
|
+
const featuresPath = path_1.default.join(process.cwd(), 'src/features');
|
|
68
|
+
if (!fs_1.default.existsSync(featuresPath)) {
|
|
69
|
+
this.spinner.info('No features directory found');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const features = fs_1.default.readdirSync(featuresPath);
|
|
73
|
+
this.report.features.count = features.length;
|
|
74
|
+
for (const feature of features) {
|
|
75
|
+
const featurePath = path_1.default.join(featuresPath, feature);
|
|
76
|
+
const requiredDirs = ['controllers', 'services', 'repositories', 'schemas'];
|
|
77
|
+
if (!requiredDirs.every(dir => fs_1.default.existsSync(path_1.default.join(featurePath, dir)))) {
|
|
78
|
+
this.report.features.incomplete.push(feature);
|
|
79
|
+
}
|
|
80
|
+
// Analyze complexity based on file count and size
|
|
81
|
+
let complexity = 0;
|
|
82
|
+
const files = this.getAllFiles(featurePath);
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
const content = fs_1.default.readFileSync(file, 'utf-8');
|
|
85
|
+
complexity += content.split('\n').length;
|
|
86
|
+
}
|
|
87
|
+
this.report.features.complexity[feature] = complexity;
|
|
88
|
+
}
|
|
89
|
+
this.spinner.succeed('Features analysis completed');
|
|
90
|
+
}
|
|
91
|
+
async analyzeDependencies() {
|
|
92
|
+
this.spinner.start('Analyzing dependencies');
|
|
93
|
+
try {
|
|
94
|
+
const { stdout: npmOutdated } = await execAsync('npm outdated --json');
|
|
95
|
+
this.report.dependencies.outdated = Object.keys(JSON.parse(npmOutdated));
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// npm outdated returns error if dependencies are outdated
|
|
99
|
+
if (error instanceof Error && 'stdout' in error) {
|
|
100
|
+
const stdout = error.stdout;
|
|
101
|
+
this.report.dependencies.outdated = Object.keys(JSON.parse(stdout));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
this.spinner.succeed('Dependencies analysis completed');
|
|
105
|
+
}
|
|
106
|
+
async analyzePerformance() {
|
|
107
|
+
this.spinner.start('Analyzing performance');
|
|
108
|
+
// Analyze API endpoints
|
|
109
|
+
const apiPath = path_1.default.join(process.cwd(), 'src/app/api');
|
|
110
|
+
if (fs_1.default.existsSync(apiPath)) {
|
|
111
|
+
this.report.performance.apiEndpoints = this.countApiEndpoints(apiPath);
|
|
112
|
+
}
|
|
113
|
+
// Calculate bundle size
|
|
114
|
+
const buildPath = path_1.default.join(process.cwd(), '.next/static');
|
|
115
|
+
if (fs_1.default.existsSync(buildPath)) {
|
|
116
|
+
this.report.performance.bundleSize = this.calculateBundleSize(buildPath);
|
|
117
|
+
}
|
|
118
|
+
this.spinner.succeed('Performance analysis completed');
|
|
119
|
+
}
|
|
120
|
+
getAllFiles(dir) {
|
|
121
|
+
const files = [];
|
|
122
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
123
|
+
for (const entry of entries) {
|
|
124
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
125
|
+
if (entry.isDirectory()) {
|
|
126
|
+
files.push(...this.getAllFiles(fullPath));
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
files.push(fullPath);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return files;
|
|
133
|
+
}
|
|
134
|
+
countApiEndpoints(dir) {
|
|
135
|
+
const files = this.getAllFiles(dir);
|
|
136
|
+
return files.filter(file => file.endsWith('route.ts')).length;
|
|
137
|
+
}
|
|
138
|
+
calculateBundleSize(dir) {
|
|
139
|
+
let totalSize = 0;
|
|
140
|
+
const files = this.getAllFiles(dir);
|
|
141
|
+
for (const file of files) {
|
|
142
|
+
const stats = fs_1.default.statSync(file);
|
|
143
|
+
totalSize += stats.size;
|
|
144
|
+
}
|
|
145
|
+
return `${Math.round(totalSize / 1024)}KB`;
|
|
146
|
+
}
|
|
147
|
+
displayReport() {
|
|
148
|
+
this.spinner.start('Generating analysis report');
|
|
149
|
+
const report = [
|
|
150
|
+
// Schema Section
|
|
151
|
+
chalk_1.default.cyan.bold('Schema Analysis'),
|
|
152
|
+
chalk_1.default.gray(`├─ Models: ${this.report.schema.models}`),
|
|
153
|
+
chalk_1.default.gray(`├─ Relations: ${this.report.schema.relations}`),
|
|
154
|
+
this.report.schema.complexModels.length > 0
|
|
155
|
+
? chalk_1.default.gray(`└─ Complex Models: ${this.report.schema.complexModels.join(', ')}`)
|
|
156
|
+
: chalk_1.default.gray('└─ No complex models found'),
|
|
157
|
+
'', // Empty line for spacing
|
|
158
|
+
// Features Section
|
|
159
|
+
chalk_1.default.cyan.bold('Features Analysis'),
|
|
160
|
+
chalk_1.default.gray(`├─ Total Features: ${this.report.features.count}`),
|
|
161
|
+
this.report.features.incomplete.length > 0
|
|
162
|
+
? chalk_1.default.gray(`└─ Incomplete Features: ${this.report.features.incomplete.join(', ')}`)
|
|
163
|
+
: chalk_1.default.gray('└─ All features are complete'),
|
|
164
|
+
'', // Empty line for spacing
|
|
165
|
+
// Dependencies Section
|
|
166
|
+
chalk_1.default.cyan.bold('Dependencies Analysis'),
|
|
167
|
+
this.report.dependencies.outdated.length > 0
|
|
168
|
+
? chalk_1.default.gray(`└─ Outdated: ${this.report.dependencies.outdated.join(', ')}`)
|
|
169
|
+
: chalk_1.default.gray('└─ All dependencies are up to date'),
|
|
170
|
+
'', // Empty line for spacing
|
|
171
|
+
// Performance Section
|
|
172
|
+
chalk_1.default.cyan.bold('Performance Analysis'),
|
|
173
|
+
chalk_1.default.gray(`├─ API Endpoints: ${this.report.performance.apiEndpoints}`),
|
|
174
|
+
chalk_1.default.gray(`└─ Bundle Size: ${this.report.performance.bundleSize}`)
|
|
175
|
+
].join('\n');
|
|
176
|
+
this.spinner.succeed('Analysis report generated');
|
|
177
|
+
// Display the report
|
|
178
|
+
console.log('\n' + report + '\n');
|
|
179
|
+
// Show recommendations if needed
|
|
180
|
+
if (this.report.schema.complexModels.length > 0 ||
|
|
181
|
+
this.report.features.incomplete.length > 0 ||
|
|
182
|
+
this.report.dependencies.outdated.length > 0) {
|
|
183
|
+
this.spinner.info(chalk_1.default.yellow('Recommendations found. Run `igniter analyze --fix` for detailed suggestions.'));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
exports.AnalyzeCommand = AnalyzeCommand;
|