create-mantiq 0.2.2 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mantiq",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Scaffold a new MantiqJS application",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -133,6 +133,29 @@ const install = Bun.spawn(['bun', 'install'], {
133
133
  await install.exited
134
134
  spin.stop('Dependencies installed')
135
135
 
136
+ // ── Install shadcn components (if selected) ─────────────────────────────────
137
+ if (ui === 'shadcn' && kit === 'react') {
138
+ const shadcnSpin = term.spinner('Installing shadcn/ui components')
139
+
140
+ const run = async (args: string[]) => {
141
+ const p = Bun.spawn(['bunx', '--bun', ...args], {
142
+ cwd: projectDir,
143
+ stdout: 'pipe',
144
+ stderr: 'pipe',
145
+ env: { ...process.env, CI: 'true' },
146
+ })
147
+ await p.exited
148
+ }
149
+
150
+ // Init shadcn
151
+ await run(['shadcn@latest', 'init', '--defaults', '--force'])
152
+
153
+ // Install core components + sidebar
154
+ await run(['shadcn@latest', 'add', 'button', 'input', 'label', 'card', 'badge', 'table', 'avatar', 'separator', 'dropdown-menu', 'sheet', 'tooltip', 'sidebar', '--overwrite', '-y'])
155
+
156
+ shadcnSpin.stop('shadcn/ui components installed')
157
+ }
158
+
136
159
  // ── Build frontend (if kit) ─────────────────────────────────────────────────
137
160
  if (kit) {
138
161
  const buildSpin = term.spinner('Building frontend assets')
package/src/ui/shadcn.ts CHANGED
@@ -6,6 +6,30 @@ export function getShadcnTemplates(ctx: TemplateContext): {
6
6
  } {
7
7
  return {
8
8
  files: {
9
+ // ── tsconfig override with @/ path alias ──────────────────────────────
10
+ 'tsconfig.json': JSON.stringify({
11
+ compilerOptions: {
12
+ target: 'ESNext',
13
+ module: 'ESNext',
14
+ moduleResolution: 'bundler',
15
+ lib: ['ESNext', 'DOM', 'DOM.Iterable'],
16
+ types: ['bun-types'],
17
+ jsx: 'react-jsx',
18
+ strict: true,
19
+ noImplicitAny: true,
20
+ strictNullChecks: true,
21
+ noUncheckedIndexedAccess: true,
22
+ noImplicitOverride: true,
23
+ allowImportingTsExtensions: true,
24
+ noEmit: true,
25
+ skipLibCheck: true,
26
+ baseUrl: '.',
27
+ paths: { '@/*': ['./src/*'] },
28
+ },
29
+ include: ['./**/*'],
30
+ exclude: ['node_modules'],
31
+ }, null, 2) + '\n',
32
+
9
33
  // ── cn helper ───────────────────────────────────────────────────────────
10
34
  'src/lib/utils.ts': `import { type ClassValue, clsx } from 'clsx'
11
35
  import { twMerge } from 'tailwind-merge'
@@ -28,8 +52,11 @@ export function cn(...inputs: ClassValue[]) {
28
52
  cssVariables: true,
29
53
  },
30
54
  aliases: {
31
- components: 'src/components',
32
- utils: 'src/lib/utils',
55
+ components: '@/components',
56
+ utils: '@/lib/utils',
57
+ ui: '@/components/ui',
58
+ lib: '@/lib',
59
+ hooks: '@/hooks',
33
60
  },
34
61
  }, null, 2) + '\n',
35
62
 
@@ -174,444 +201,17 @@ export function cn(...inputs: ClassValue[]) {
174
201
  .animate-fade-up { animation: fadeUp 0.4s ease-out; }
175
202
  `,
176
203
 
177
- // ── Button component ────────────────────────────────────────────────────
178
- 'src/components/ui/button.tsx': `import * as React from 'react'
179
- import { Slot } from '@radix-ui/react-slot'
180
- import { cva, type VariantProps } from 'class-variance-authority'
181
- import { cn } from '../../lib/utils.ts'
182
-
183
- const buttonVariants = cva(
184
- 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
185
- {
186
- variants: {
187
- variant: {
188
- default:
189
- 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
190
- destructive:
191
- 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
192
- outline:
193
- 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
194
- secondary:
195
- 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
196
- ghost: 'hover:bg-accent hover:text-accent-foreground',
197
- link: 'text-primary underline-offset-4 hover:underline',
198
- },
199
- size: {
200
- default: 'h-9 px-4 py-2',
201
- sm: 'h-8 rounded-md px-3 text-xs',
202
- lg: 'h-10 rounded-md px-8',
203
- icon: 'h-9 w-9',
204
- },
205
- },
206
- defaultVariants: {
207
- variant: 'default',
208
- size: 'default',
209
- },
210
- }
211
- )
212
-
213
- export interface ButtonProps
214
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
215
- VariantProps<typeof buttonVariants> {
216
- asChild?: boolean
217
- }
218
-
219
- const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
220
- ({ className, variant, size, asChild = false, ...props }, ref) => {
221
- const Comp = asChild ? Slot : 'button'
222
- return (
223
- <Comp
224
- className={cn(buttonVariants({ variant, size, className }))}
225
- ref={ref}
226
- {...props}
227
- />
228
- )
229
- }
230
- )
231
- Button.displayName = 'Button'
232
-
233
- export { Button, buttonVariants }
234
- `,
235
-
236
- // ── Input component ─────────────────────────────────────────────────────
237
- 'src/components/ui/input.tsx': `import * as React from 'react'
238
- import { cn } from '../../lib/utils.ts'
239
-
240
- export interface InputProps
241
- extends React.InputHTMLAttributes<HTMLInputElement> {}
242
-
243
- const Input = React.forwardRef<HTMLInputElement, InputProps>(
244
- ({ className, type, ...props }, ref) => {
245
- return (
246
- <input
247
- type={type}
248
- className={cn(
249
- 'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
250
- className
251
- )}
252
- ref={ref}
253
- {...props}
254
- />
255
- )
256
- }
257
- )
258
- Input.displayName = 'Input'
259
-
260
- export { Input }
261
- `,
262
-
263
- // ── Label component ─────────────────────────────────────────────────────
264
- 'src/components/ui/label.tsx': `import * as React from 'react'
265
- import * as LabelPrimitive from '@radix-ui/react-label'
266
- import { cva, type VariantProps } from 'class-variance-authority'
267
- import { cn } from '../../lib/utils.ts'
268
-
269
- const labelVariants = cva(
270
- 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
271
- )
272
-
273
- const Label = React.forwardRef<
274
- React.ElementRef<typeof LabelPrimitive.Root>,
275
- React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
276
- VariantProps<typeof labelVariants>
277
- >(({ className, ...props }, ref) => (
278
- <LabelPrimitive.Root
279
- ref={ref}
280
- className={cn(labelVariants(), className)}
281
- {...props}
282
- />
283
- ))
284
- Label.displayName = LabelPrimitive.Root.displayName
285
-
286
- export { Label }
287
- `,
288
-
289
- // ── Card component ──────────────────────────────────────────────────────
290
- 'src/components/ui/card.tsx': `import * as React from 'react'
291
- import { cn } from '../../lib/utils.ts'
292
-
293
- const Card = React.forwardRef<
294
- HTMLDivElement,
295
- React.HTMLAttributes<HTMLDivElement>
296
- >(({ className, ...props }, ref) => (
297
- <div
298
- ref={ref}
299
- className={cn(
300
- 'rounded-xl border border-border bg-card text-card-foreground shadow',
301
- className
302
- )}
303
- {...props}
304
- />
305
- ))
306
- Card.displayName = 'Card'
307
-
308
- const CardHeader = React.forwardRef<
309
- HTMLDivElement,
310
- React.HTMLAttributes<HTMLDivElement>
311
- >(({ className, ...props }, ref) => (
312
- <div
313
- ref={ref}
314
- className={cn('flex flex-col space-y-1.5 p-6', className)}
315
- {...props}
316
- />
317
- ))
318
- CardHeader.displayName = 'CardHeader'
319
-
320
- const CardTitle = React.forwardRef<
321
- HTMLDivElement,
322
- React.HTMLAttributes<HTMLDivElement>
323
- >(({ className, ...props }, ref) => (
324
- <div
325
- ref={ref}
326
- className={cn('font-semibold leading-none tracking-tight', className)}
327
- {...props}
328
- />
329
- ))
330
- CardTitle.displayName = 'CardTitle'
331
-
332
- const CardDescription = React.forwardRef<
333
- HTMLDivElement,
334
- React.HTMLAttributes<HTMLDivElement>
335
- >(({ className, ...props }, ref) => (
336
- <div
337
- ref={ref}
338
- className={cn('text-sm text-muted-foreground', className)}
339
- {...props}
340
- />
341
- ))
342
- CardDescription.displayName = 'CardDescription'
343
-
344
- const CardContent = React.forwardRef<
345
- HTMLDivElement,
346
- React.HTMLAttributes<HTMLDivElement>
347
- >(({ className, ...props }, ref) => (
348
- <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
349
- ))
350
- CardContent.displayName = 'CardContent'
351
-
352
- const CardFooter = React.forwardRef<
353
- HTMLDivElement,
354
- React.HTMLAttributes<HTMLDivElement>
355
- >(({ className, ...props }, ref) => (
356
- <div
357
- ref={ref}
358
- className={cn('flex items-center p-6 pt-0', className)}
359
- {...props}
360
- />
361
- ))
362
- CardFooter.displayName = 'CardFooter'
363
-
364
- export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
365
- `,
366
-
367
- // ── Badge component ─────────────────────────────────────────────────────
368
- 'src/components/ui/badge.tsx': `import * as React from 'react'
369
- import { cva, type VariantProps } from 'class-variance-authority'
370
- import { cn } from '../../lib/utils.ts'
371
-
372
- const badgeVariants = cva(
373
- 'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
374
- {
375
- variants: {
376
- variant: {
377
- default:
378
- 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
379
- secondary:
380
- 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
381
- destructive:
382
- 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
383
- outline: 'text-foreground',
384
- },
385
- },
386
- defaultVariants: {
387
- variant: 'default',
388
- },
389
- }
390
- )
391
-
392
- export interface BadgeProps
393
- extends React.HTMLAttributes<HTMLDivElement>,
394
- VariantProps<typeof badgeVariants> {}
395
-
396
- function Badge({ className, variant, ...props }: BadgeProps) {
397
- return (
398
- <div className={cn(badgeVariants({ variant }), className)} {...props} />
399
- )
400
- }
401
-
402
- export { Badge, badgeVariants }
403
- `,
404
-
405
- // ── Table component ─────────────────────────────────────────────────────
406
- 'src/components/ui/table.tsx': `import * as React from 'react'
407
- import { cn } from '../../lib/utils.ts'
408
-
409
- const Table = React.forwardRef<
410
- HTMLTableElement,
411
- React.HTMLAttributes<HTMLTableElement>
412
- >(({ className, ...props }, ref) => (
413
- <div className="relative w-full overflow-auto">
414
- <table
415
- ref={ref}
416
- className={cn('w-full caption-bottom text-sm', className)}
417
- {...props}
418
- />
419
- </div>
420
- ))
421
- Table.displayName = 'Table'
422
-
423
- const TableHeader = React.forwardRef<
424
- HTMLTableSectionElement,
425
- React.HTMLAttributes<HTMLTableSectionElement>
426
- >(({ className, ...props }, ref) => (
427
- <thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
428
- ))
429
- TableHeader.displayName = 'TableHeader'
430
-
431
- const TableBody = React.forwardRef<
432
- HTMLTableSectionElement,
433
- React.HTMLAttributes<HTMLTableSectionElement>
434
- >(({ className, ...props }, ref) => (
435
- <tbody
436
- ref={ref}
437
- className={cn('[&_tr:last-child]:border-0', className)}
438
- {...props}
439
- />
440
- ))
441
- TableBody.displayName = 'TableBody'
442
-
443
- const TableFooter = React.forwardRef<
444
- HTMLTableSectionElement,
445
- React.HTMLAttributes<HTMLTableSectionElement>
446
- >(({ className, ...props }, ref) => (
447
- <tfoot
448
- ref={ref}
449
- className={cn(
450
- 'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
451
- className
452
- )}
453
- {...props}
454
- />
455
- ))
456
- TableFooter.displayName = 'TableFooter'
457
-
458
- const TableRow = React.forwardRef<
459
- HTMLTableRowElement,
460
- React.HTMLAttributes<HTMLTableRowElement>
461
- >(({ className, ...props }, ref) => (
462
- <tr
463
- ref={ref}
464
- className={cn(
465
- 'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
466
- className
467
- )}
468
- {...props}
469
- />
470
- ))
471
- TableRow.displayName = 'TableRow'
472
-
473
- const TableHead = React.forwardRef<
474
- HTMLTableCellElement,
475
- React.ThHTMLAttributes<HTMLTableCellElement>
476
- >(({ className, ...props }, ref) => (
477
- <th
478
- ref={ref}
479
- className={cn(
480
- 'h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
481
- className
482
- )}
483
- {...props}
484
- />
485
- ))
486
- TableHead.displayName = 'TableHead'
487
-
488
- const TableCell = React.forwardRef<
489
- HTMLTableCellElement,
490
- React.TdHTMLAttributes<HTMLTableCellElement>
491
- >(({ className, ...props }, ref) => (
492
- <td
493
- ref={ref}
494
- className={cn(
495
- 'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
496
- className
497
- )}
498
- {...props}
499
- />
500
- ))
501
- TableCell.displayName = 'TableCell'
502
-
503
- const TableCaption = React.forwardRef<
504
- HTMLTableCaptionElement,
505
- React.HTMLAttributes<HTMLTableCaptionElement>
506
- >(({ className, ...props }, ref) => (
507
- <caption
508
- ref={ref}
509
- className={cn('mt-4 text-sm text-muted-foreground', className)}
510
- {...props}
511
- />
512
- ))
513
- TableCaption.displayName = 'TableCaption'
514
-
515
- export {
516
- Table,
517
- TableHeader,
518
- TableBody,
519
- TableFooter,
520
- TableHead,
521
- TableRow,
522
- TableCell,
523
- TableCaption,
524
- }
525
- `,
526
-
527
- // ── Avatar component ────────────────────────────────────────────────────
528
- 'src/components/ui/avatar.tsx': `import * as React from 'react'
529
- import { cn } from '../../lib/utils.ts'
530
-
531
- const Avatar = React.forwardRef<
532
- HTMLDivElement,
533
- React.HTMLAttributes<HTMLDivElement>
534
- >(({ className, ...props }, ref) => (
535
- <div
536
- ref={ref}
537
- className={cn(
538
- 'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
539
- className
540
- )}
541
- {...props}
542
- />
543
- ))
544
- Avatar.displayName = 'Avatar'
545
-
546
- const AvatarImage = React.forwardRef<
547
- HTMLImageElement,
548
- React.ImgHTMLAttributes<HTMLImageElement>
549
- >(({ className, ...props }, ref) => (
550
- <img
551
- ref={ref}
552
- className={cn('aspect-square h-full w-full', className)}
553
- {...props}
554
- />
555
- ))
556
- AvatarImage.displayName = 'AvatarImage'
557
-
558
- const AvatarFallback = React.forwardRef<
559
- HTMLDivElement,
560
- React.HTMLAttributes<HTMLDivElement>
561
- >(({ className, ...props }, ref) => (
562
- <div
563
- ref={ref}
564
- className={cn(
565
- 'flex h-full w-full items-center justify-center rounded-full bg-muted',
566
- className
567
- )}
568
- {...props}
569
- />
570
- ))
571
- AvatarFallback.displayName = 'AvatarFallback'
572
-
573
- export { Avatar, AvatarImage, AvatarFallback }
574
- `,
575
-
576
- // ── Separator component ─────────────────────────────────────────────────
577
- 'src/components/ui/separator.tsx': `import * as React from 'react'
578
- import { cn } from '../../lib/utils.ts'
579
-
580
- interface SeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
581
- orientation?: 'horizontal' | 'vertical'
582
- decorative?: boolean
583
- }
584
-
585
- const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(
586
- (
587
- { className, orientation = 'horizontal', decorative = true, ...props },
588
- ref
589
- ) => (
590
- <div
591
- ref={ref}
592
- role={decorative ? 'none' : 'separator'}
593
- aria-orientation={decorative ? undefined : orientation}
594
- className={cn(
595
- 'shrink-0 bg-border',
596
- orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
597
- className
598
- )}
599
- {...props}
600
- />
601
- )
602
- )
603
- Separator.displayName = 'Separator'
604
-
605
- export { Separator }
606
- `,
204
+ // Components (button, input, label, card, badge, table, avatar,
205
+ // separator, sidebar, etc.) are installed by the shadcn CLI during
206
+ // scaffold no hand-rolled templates needed.
607
207
 
608
208
  // ── Login page (shadcn) ─────────────────────────────────────────────────
609
209
  'src/pages/Login.tsx': `import { useState } from 'react'
610
210
  import { post } from '../lib/api.ts'
611
- import { Button } from '../components/ui/button.tsx'
612
- import { Input } from '../components/ui/input.tsx'
613
- import { Label } from '../components/ui/label.tsx'
614
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card.tsx'
211
+ import { Button } from '@/components/ui/button'
212
+ import { Input } from '@/components/ui/input'
213
+ import { Label } from '@/components/ui/label'
214
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
615
215
 
616
216
  interface LoginProps {
617
217
  appName?: string
@@ -698,10 +298,10 @@ export default function Login({ appName = '${ctx.name}', navigate }: LoginProps)
698
298
  // ── Register page (shadcn) ──────────────────────────────────────────────
699
299
  'src/pages/Register.tsx': `import { useState } from 'react'
700
300
  import { post } from '../lib/api.ts'
701
- import { Button } from '../components/ui/button.tsx'
702
- import { Input } from '../components/ui/input.tsx'
703
- import { Label } from '../components/ui/label.tsx'
704
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card.tsx'
301
+ import { Button } from '@/components/ui/button'
302
+ import { Input } from '@/components/ui/input'
303
+ import { Label } from '@/components/ui/label'
304
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
705
305
 
706
306
  interface RegisterProps {
707
307
  appName?: string
@@ -799,11 +399,11 @@ export default function Register({ appName = '${ctx.name}', navigate }: Register
799
399
  // ── Dashboard page (shadcn) ─────────────────────────────────────────────
800
400
  'src/pages/Dashboard.tsx': `import { useState, useEffect, useCallback } from 'react'
801
401
  import { api, post } from '../lib/api.ts'
802
- import { Button } from '../components/ui/button.tsx'
803
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card.tsx'
804
- import { Badge } from '../components/ui/badge.tsx'
805
- import { Avatar, AvatarFallback } from '../components/ui/avatar.tsx'
806
- import { Separator } from '../components/ui/separator.tsx'
402
+ import { Button } from '@/components/ui/button'
403
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
404
+ import { Badge } from '@/components/ui/badge'
405
+ import { Avatar, AvatarFallback } from '@/components/ui/avatar'
406
+ import { Separator } from '@/components/ui/separator'
807
407
  import {
808
408
  Table,
809
409
  TableBody,
@@ -811,7 +411,7 @@ import {
811
411
  TableHead,
812
412
  TableHeader,
813
413
  TableRow,
814
- } from '../components/ui/table.tsx'
414
+ } from '@/components/ui/table'
815
415
 
816
416
  interface User { id: number; name: string; email: string; role: string }
817
417
 
@@ -1022,8 +622,6 @@ export default function Dashboard({ appName = '${ctx.name}', currentUser, users:
1022
622
  'tailwind-merge': '^2.6.0',
1023
623
  'class-variance-authority': '^0.7.0',
1024
624
  'lucide-react': '^0.460.0',
1025
- '@radix-ui/react-slot': '^1.1.0',
1026
- '@radix-ui/react-label': '^2.1.0',
1027
625
  },
1028
626
  }
1029
627
  }