@tioelvis/next-template 1.0.8 → 2.0.2
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 +3 -1
- package/src/app/components/ui/accordion.json +6 -0
- package/src/app/components/ui/alert-dialog.json +6 -0
- package/src/app/components/ui/alert-dialog.tsx +157 -0
- package/src/app/components/ui/alert.json +6 -0
- package/src/app/components/ui/aspect-ratio.json +6 -0
- package/src/app/components/ui/aspect-ratio.tsx +11 -0
- package/src/app/components/ui/button.json +6 -0
- package/src/app/components/ui/button.tsx +59 -0
- package/src/constants.js +51 -0
- package/src/main.js +196 -91
- package/src/utils.js +113 -0
- package/src/lib/constants.js +0 -28
- package/src/lib/utils.js +0 -144
- package/src/ui/components/accordion.json +0 -1
- package/src/ui/components/alert.json +0 -1
- /package/src/{ui/components → app/components/ui}/accordion.tsx +0 -0
- /package/src/{ui/components → app/components/ui}/alert.tsx +0 -0
- /package/src/{ui → app}/lib/utils.ts +0 -0
- /package/src/{ui → app}/tsconfig.json +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tioelvis/next-template",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "CLI to scaffold a Next.js + Tailwind project using shadcn/ui components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@eslint/eslintrc": "^3.3.1",
|
|
33
33
|
"@radix-ui/react-accordion": "^1.2.11",
|
|
34
|
+
"@radix-ui/react-alert-dialog": "^1.1.14",
|
|
35
|
+
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
|
34
36
|
"@tailwindcss/postcss": "^4.1.11",
|
|
35
37
|
"@tanstack/react-query": "^5.83.0",
|
|
36
38
|
"@types/node": "^24.1.0",
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
import { buttonVariants } from "@/components/ui/button";
|
|
8
|
+
|
|
9
|
+
function AlertDialog({
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
|
12
|
+
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function AlertDialogTrigger({
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
|
18
|
+
return (
|
|
19
|
+
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function AlertDialogPortal({
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
|
26
|
+
return (
|
|
27
|
+
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function AlertDialogOverlay({
|
|
32
|
+
className,
|
|
33
|
+
...props
|
|
34
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
|
35
|
+
return (
|
|
36
|
+
<AlertDialogPrimitive.Overlay
|
|
37
|
+
data-slot="alert-dialog-overlay"
|
|
38
|
+
className={cn(
|
|
39
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
40
|
+
className
|
|
41
|
+
)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function AlertDialogContent({
|
|
48
|
+
className,
|
|
49
|
+
...props
|
|
50
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
|
51
|
+
return (
|
|
52
|
+
<AlertDialogPortal>
|
|
53
|
+
<AlertDialogOverlay />
|
|
54
|
+
<AlertDialogPrimitive.Content
|
|
55
|
+
data-slot="alert-dialog-content"
|
|
56
|
+
className={cn(
|
|
57
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
|
58
|
+
className
|
|
59
|
+
)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
</AlertDialogPortal>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function AlertDialogHeader({
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: React.ComponentProps<"div">) {
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
data-slot="alert-dialog-header"
|
|
73
|
+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function AlertDialogFooter({
|
|
80
|
+
className,
|
|
81
|
+
...props
|
|
82
|
+
}: React.ComponentProps<"div">) {
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
data-slot="alert-dialog-footer"
|
|
86
|
+
className={cn(
|
|
87
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
88
|
+
className
|
|
89
|
+
)}
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function AlertDialogTitle({
|
|
96
|
+
className,
|
|
97
|
+
...props
|
|
98
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
|
99
|
+
return (
|
|
100
|
+
<AlertDialogPrimitive.Title
|
|
101
|
+
data-slot="alert-dialog-title"
|
|
102
|
+
className={cn("text-lg font-semibold", className)}
|
|
103
|
+
{...props}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function AlertDialogDescription({
|
|
109
|
+
className,
|
|
110
|
+
...props
|
|
111
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
|
112
|
+
return (
|
|
113
|
+
<AlertDialogPrimitive.Description
|
|
114
|
+
data-slot="alert-dialog-description"
|
|
115
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
116
|
+
{...props}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function AlertDialogAction({
|
|
122
|
+
className,
|
|
123
|
+
...props
|
|
124
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
|
125
|
+
return (
|
|
126
|
+
<AlertDialogPrimitive.Action
|
|
127
|
+
className={cn(buttonVariants(), className)}
|
|
128
|
+
{...props}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function AlertDialogCancel({
|
|
134
|
+
className,
|
|
135
|
+
...props
|
|
136
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
|
137
|
+
return (
|
|
138
|
+
<AlertDialogPrimitive.Cancel
|
|
139
|
+
className={cn(buttonVariants({ variant: "outline" }), className)}
|
|
140
|
+
{...props}
|
|
141
|
+
/>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export {
|
|
146
|
+
AlertDialog,
|
|
147
|
+
AlertDialogPortal,
|
|
148
|
+
AlertDialogOverlay,
|
|
149
|
+
AlertDialogTrigger,
|
|
150
|
+
AlertDialogContent,
|
|
151
|
+
AlertDialogHeader,
|
|
152
|
+
AlertDialogFooter,
|
|
153
|
+
AlertDialogTitle,
|
|
154
|
+
AlertDialogDescription,
|
|
155
|
+
AlertDialogAction,
|
|
156
|
+
AlertDialogCancel,
|
|
157
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio";
|
|
4
|
+
|
|
5
|
+
function AspectRatio({
|
|
6
|
+
...props
|
|
7
|
+
}: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
|
|
8
|
+
return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { AspectRatio };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
16
|
+
outline:
|
|
17
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
20
|
+
ghost:
|
|
21
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
22
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
26
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
27
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
28
|
+
icon: "size-9",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: {
|
|
32
|
+
variant: "default",
|
|
33
|
+
size: "default",
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
function Button({
|
|
39
|
+
className,
|
|
40
|
+
variant,
|
|
41
|
+
size,
|
|
42
|
+
asChild = false,
|
|
43
|
+
...props
|
|
44
|
+
}: React.ComponentProps<"button"> &
|
|
45
|
+
VariantProps<typeof buttonVariants> & {
|
|
46
|
+
asChild?: boolean;
|
|
47
|
+
}) {
|
|
48
|
+
const Comp = asChild ? Slot : "button";
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Comp
|
|
52
|
+
data-slot="button"
|
|
53
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { Button, buttonVariants };
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = path.dirname(__filename);
|
|
6
|
+
|
|
7
|
+
const TEMPLATE_PATH = path.join(__dirname, "template");
|
|
8
|
+
const COMPONENTS_PATH = path.join(__dirname, "app", "components", "ui");
|
|
9
|
+
const HOOKS_PATH = path.join(__dirname, "app", "hooks");
|
|
10
|
+
|
|
11
|
+
const DEPENDENCIES = [
|
|
12
|
+
"@tanstack/react-query",
|
|
13
|
+
"axios",
|
|
14
|
+
"class-variance-authority",
|
|
15
|
+
"clsx",
|
|
16
|
+
"cookies-next",
|
|
17
|
+
"date-fns",
|
|
18
|
+
"lucide-react",
|
|
19
|
+
"next",
|
|
20
|
+
"next-auth",
|
|
21
|
+
"next-themes",
|
|
22
|
+
"react",
|
|
23
|
+
"react-dom",
|
|
24
|
+
"tailwind-merge",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const DEV_DEPENDENCIES = [
|
|
28
|
+
"@eslint/eslintrc",
|
|
29
|
+
"@tailwindcss/postcss",
|
|
30
|
+
"@types/node",
|
|
31
|
+
"@types/react",
|
|
32
|
+
"@types/react-dom",
|
|
33
|
+
"eslint",
|
|
34
|
+
"eslint-config-next",
|
|
35
|
+
"tailwindcss",
|
|
36
|
+
"tw-animate-css",
|
|
37
|
+
"typescript",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const COMPONENTS = ["accordion", "alert", "alert-dialog", "aspect-ratio"];
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
__dirname,
|
|
44
|
+
__filename,
|
|
45
|
+
TEMPLATE_PATH,
|
|
46
|
+
COMPONENTS_PATH,
|
|
47
|
+
HOOKS_PATH,
|
|
48
|
+
DEPENDENCIES,
|
|
49
|
+
DEV_DEPENDENCIES,
|
|
50
|
+
COMPONENTS,
|
|
51
|
+
};
|
package/src/main.js
CHANGED
|
@@ -1,113 +1,218 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "./lib/utils.js";
|
|
4
|
+
copy_components,
|
|
5
|
+
copy_template,
|
|
6
|
+
on_cancel,
|
|
7
|
+
run_command,
|
|
8
|
+
} from "./utils.js";
|
|
9
9
|
import fs from "node:fs";
|
|
10
10
|
import chalk from "chalk";
|
|
11
11
|
import path from "node:path";
|
|
12
|
-
import
|
|
13
|
-
import { DEPENDENCIES, DEV_DEPENDENCIES } from "./
|
|
14
|
-
|
|
15
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
import prompts from "prompts";
|
|
13
|
+
import { COMPONENTS, DEPENDENCIES, DEV_DEPENDENCIES } from "./constants.js";
|
|
16
14
|
|
|
17
15
|
// Manage Ctrl+C command
|
|
18
|
-
process.on("SIGINT", () =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
process.on("SIGINT", () => on_cancel());
|
|
17
|
+
|
|
18
|
+
(async function () {
|
|
19
|
+
let DEST;
|
|
20
|
+
const CWD = path.resolve(process.cwd());
|
|
21
|
+
|
|
22
|
+
const { is_new_project } = await prompts(
|
|
23
|
+
{
|
|
24
|
+
type: "confirm",
|
|
25
|
+
initial: true,
|
|
26
|
+
name: "is_new_project",
|
|
27
|
+
message: "Is a new project?",
|
|
28
|
+
},
|
|
29
|
+
{ onCancel: on_cancel }
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (is_new_project === true) {
|
|
33
|
+
try {
|
|
34
|
+
if (fs.existsSync(path.join(CWD, "next.config.ts")) === true) {
|
|
35
|
+
throw new Error("Config file already exists");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const responses = await prompts(
|
|
39
|
+
[
|
|
40
|
+
{
|
|
41
|
+
type: "text",
|
|
42
|
+
name: "project_name",
|
|
43
|
+
message: "What is your project named?",
|
|
44
|
+
validate: (e) => {
|
|
45
|
+
if (e === undefined || e.trim() === "") {
|
|
46
|
+
return chalk.red("⚠️ The name is required");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(e.trim())) {
|
|
50
|
+
return chalk.red(
|
|
51
|
+
"⚠️ Use only letters, numbers, dashes, and underscores"
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return true;
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: "select",
|
|
60
|
+
name: "package_manager",
|
|
61
|
+
message: "Which package manager would you want to use?",
|
|
62
|
+
choices: [
|
|
63
|
+
{ title: "npm", value: "npm" },
|
|
64
|
+
{ title: "pnpm", value: "pnpm" },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: "multiselect",
|
|
69
|
+
name: "components",
|
|
70
|
+
message: "Which components do you want to install?",
|
|
71
|
+
instructions: false,
|
|
72
|
+
choices: COMPONENTS.map((e) => {
|
|
73
|
+
return { title: e, value: e };
|
|
74
|
+
}),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
{ onCancel: on_cancel }
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const { project_name, package_manager, components } = responses;
|
|
81
|
+
|
|
82
|
+
// Creating folder and copying files
|
|
83
|
+
DEST = path.resolve(CWD, project_name);
|
|
84
|
+
|
|
85
|
+
if (fs.existsSync(DEST) === true) {
|
|
86
|
+
throw new Error("A folder with that name already exists");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log(chalk.blue("📁 Creating project..."));
|
|
90
|
+
|
|
91
|
+
fs.mkdirSync(DEST, { recursive: true });
|
|
92
|
+
|
|
93
|
+
copy_template(DEST);
|
|
94
|
+
await copy_components(DEST, components);
|
|
22
95
|
|
|
23
|
-
|
|
24
|
-
|
|
96
|
+
// Initialize package.json
|
|
97
|
+
console.log(chalk.blue("📦 Initializing and configure package.json..."));
|
|
25
98
|
|
|
26
|
-
|
|
27
|
-
|
|
99
|
+
const package_json = {
|
|
100
|
+
name: project_name,
|
|
101
|
+
version: "1.0.0",
|
|
102
|
+
private: true,
|
|
103
|
+
scripts: {
|
|
104
|
+
dev: "next dev --turbopack",
|
|
105
|
+
build: "next build",
|
|
106
|
+
start: "next start",
|
|
107
|
+
lint: "next lint",
|
|
108
|
+
},
|
|
109
|
+
};
|
|
28
110
|
|
|
29
|
-
|
|
30
|
-
|
|
111
|
+
const package_json_path = path.join(DEST, "package.json");
|
|
112
|
+
|
|
113
|
+
fs.writeFileSync(
|
|
114
|
+
package_json_path,
|
|
115
|
+
JSON.stringify(package_json, null, 2)
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Install dependencies
|
|
119
|
+
console.log(chalk.cyan("⬇️ Installing dependencies..."));
|
|
120
|
+
|
|
121
|
+
if (package_manager === "npm") {
|
|
122
|
+
run_command(`npm install ${DEPENDENCIES.join(" ")}`, DEST);
|
|
123
|
+
run_command(`npm install ${DEV_DEPENDENCIES.join(" ")} -D`, DEST);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (package_manager === "pnpm") {
|
|
127
|
+
run_command(`pnpm add ${DEPENDENCIES.join(" ")}`, DEST);
|
|
128
|
+
run_command(`pnpm add ${DEV_DEPENDENCIES.join(" ")} -D`, DEST);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Finish project
|
|
132
|
+
console.log(
|
|
133
|
+
chalk.yellow(
|
|
134
|
+
"📄 IMPORTANT: make sure to add your variable NEXT_PUBLIC_API in the .env file, e.g.: NEXT_PUBLIC_API=http://localhost:9000"
|
|
135
|
+
)
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
console.log(
|
|
139
|
+
chalk.green(`\n✅ Project ${project_name} created successfully! 🚀`)
|
|
140
|
+
);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error(chalk.red("❌ Internal error:"), error.message);
|
|
143
|
+
|
|
144
|
+
if (fs.existsSync(DEST) === true) {
|
|
145
|
+
fs.rmSync(DEST, { recursive: true, force: true });
|
|
146
|
+
}
|
|
31
147
|
|
|
32
|
-
if (fs.existsSync(project_path)) {
|
|
33
|
-
console.error(chalk.red("❌ A folder with that name already exists"));
|
|
34
148
|
process.exit(1);
|
|
35
149
|
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (is_new_project === false) {
|
|
153
|
+
let package_manager;
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
if (fs.existsSync(path.join(CWD, "next.config.ts")) === false) {
|
|
157
|
+
throw new Error("Config file not exists");
|
|
158
|
+
}
|
|
36
159
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
private: true,
|
|
58
|
-
scripts: {
|
|
59
|
-
dev: "next dev --turbopack",
|
|
60
|
-
build: "next build",
|
|
61
|
-
start: "next start",
|
|
62
|
-
lint: "next lint",
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const package_json_path = path.join(project_path, "package.json");
|
|
67
|
-
fs.writeFileSync(package_json_path, JSON.stringify(package_json, null, 2));
|
|
68
|
-
|
|
69
|
-
// Install dependencies
|
|
70
|
-
console.log(chalk.cyan("⬇️ Installing dependencies..."));
|
|
71
|
-
|
|
72
|
-
components.forEach((e) => {
|
|
73
|
-
const component_path = path.resolve(components_path, `${e}.json`);
|
|
74
|
-
|
|
75
|
-
if (fs.existsSync(component_path) === false) {
|
|
76
|
-
throw new Error(`Component ${e} not found`);
|
|
160
|
+
const responses = await prompts(
|
|
161
|
+
[
|
|
162
|
+
{
|
|
163
|
+
type: "multiselect",
|
|
164
|
+
name: "components",
|
|
165
|
+
message: "Which components do you want to install?",
|
|
166
|
+
instructions: false,
|
|
167
|
+
choices: COMPONENTS.map((e) => {
|
|
168
|
+
return { title: e, value: e };
|
|
169
|
+
}),
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
{ onCancel: on_cancel }
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const { components } = responses;
|
|
176
|
+
|
|
177
|
+
// Set package manager
|
|
178
|
+
if (fs.existsSync(path.join(CWD, "package-lock.json")) === true) {
|
|
179
|
+
package_manager = "npm";
|
|
77
180
|
}
|
|
78
181
|
|
|
79
|
-
|
|
182
|
+
if (fs.existsSync(path.join(CWD, "pnpm-lock.yaml")) === true) {
|
|
183
|
+
package_manager = "pnpm";
|
|
184
|
+
}
|
|
80
185
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
});
|
|
186
|
+
if (package_manager === undefined) {
|
|
187
|
+
throw new Error("Not package_manager found");
|
|
188
|
+
}
|
|
85
189
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
run(`npm install ${DEV_DEPENDENCIES.join(" ")} -D`, project_path);
|
|
89
|
-
}
|
|
190
|
+
// Copying files
|
|
191
|
+
DEST = path.resolve(CWD);
|
|
90
192
|
|
|
91
|
-
|
|
92
|
-
run(`pnpm add ${DEPENDENCIES.join(" ")}`, project_path);
|
|
93
|
-
run(`pnpm add ${DEV_DEPENDENCIES.join(" ")} -D`, project_path);
|
|
94
|
-
}
|
|
193
|
+
await copy_components(DEST, components);
|
|
95
194
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
195
|
+
// Install dependencies
|
|
196
|
+
console.log(chalk.cyan("⬇️ Installing dependencies..."));
|
|
197
|
+
|
|
198
|
+
if (package_manager === "npm") {
|
|
199
|
+
run_command(`npm install ${DEPENDENCIES.join(" ")}`, DEST);
|
|
200
|
+
run_command(`npm install ${DEV_DEPENDENCIES.join(" ")} -D`, DEST);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (package_manager === "pnpm") {
|
|
204
|
+
run_command(`pnpm add ${DEPENDENCIES.join(" ")}`, DEST);
|
|
205
|
+
run_command(`pnpm add ${DEV_DEPENDENCIES.join(" ")} -D`, DEST);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Finish project
|
|
209
|
+
console.log(chalk.green("\n✅ Components add successfully!"));
|
|
210
|
+
} catch (error) {
|
|
211
|
+
console.error(chalk.red("❌ Internal error:"), error.message);
|
|
212
|
+
|
|
213
|
+
process.exit(1);
|
|
108
214
|
}
|
|
109
|
-
process.exit(1);
|
|
110
215
|
}
|
|
111
|
-
}
|
|
112
216
|
|
|
113
|
-
|
|
217
|
+
process.exit(0);
|
|
218
|
+
})();
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COMPONENTS_PATH,
|
|
3
|
+
DEPENDENCIES,
|
|
4
|
+
DEV_DEPENDENCIES,
|
|
5
|
+
HOOKS_PATH,
|
|
6
|
+
TEMPLATE_PATH,
|
|
7
|
+
} from "./constants.js";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import prompts from "prompts";
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
13
|
+
|
|
14
|
+
function on_cancel() {
|
|
15
|
+
console.log(chalk.yellow("\n👋 Exiting..."));
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function run_command(cmd, cwd) {
|
|
20
|
+
try {
|
|
21
|
+
execSync(cmd, { cwd, stdio: "inherit" });
|
|
22
|
+
} catch (error) {
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function copy_template(dest) {
|
|
28
|
+
try {
|
|
29
|
+
fs.cpSync(TEMPLATE_PATH, dest, { recursive: true });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function copy_components(DEST, components) {
|
|
36
|
+
try {
|
|
37
|
+
for (const component of components) {
|
|
38
|
+
let replace = false;
|
|
39
|
+
|
|
40
|
+
const component_path = path.resolve(COMPONENTS_PATH, `${component}.tsx`);
|
|
41
|
+
const dest_folder = path.resolve(DEST, "src", "components", "ui");
|
|
42
|
+
const final_dest = path.join(dest_folder, `${component}.tsx`);
|
|
43
|
+
|
|
44
|
+
if (fs.existsSync(component_path) === false) {
|
|
45
|
+
throw new Error(`Component ${component} not found`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (fs.existsSync(dest_folder) === false) {
|
|
49
|
+
fs.mkdirSync(dest_folder, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (fs.existsSync(final_dest) === true) {
|
|
53
|
+
const response = await prompts(
|
|
54
|
+
[
|
|
55
|
+
{
|
|
56
|
+
type: "confirm",
|
|
57
|
+
name: "replace",
|
|
58
|
+
message: `Do you want to replace ${component}.tsx?`,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
{ onCancel: on_cancel }
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
replace = response.replace;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const json_path = path.resolve(COMPONENTS_PATH, `${component}.json`);
|
|
68
|
+
|
|
69
|
+
if (fs.existsSync(json_path) === false) {
|
|
70
|
+
throw new Error(`Json file of ${component} not found`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const json = JSON.parse(fs.readFileSync(json_path, "utf8"));
|
|
74
|
+
|
|
75
|
+
if (fs.existsSync(final_dest) === false || replace === true) {
|
|
76
|
+
console.log(chalk.gray(`📄 Add ${component}.tsx file`));
|
|
77
|
+
fs.copyFileSync(component_path, final_dest);
|
|
78
|
+
|
|
79
|
+
for (const hook of json.hooks) {
|
|
80
|
+
const hook_path = path.resolve(HOOKS_PATH, `${hook}.ts`);
|
|
81
|
+
const dest_folder = path.resolve(DEST, "src", "hooks");
|
|
82
|
+
const final_dest = path.join(dest_folder, `${hook}.ts`);
|
|
83
|
+
|
|
84
|
+
if (fs.existsSync(hook_path) === false) {
|
|
85
|
+
throw new Error(`Hook ${hook} not found for component ${e}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (fs.existsSync(dest_folder) === false) {
|
|
89
|
+
fs.mkdirSync(dest_folder, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fs.copyFileSync(hook_path, final_dest);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const support of json.supports) {
|
|
97
|
+
await copy_components(DEST, [support]);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const dependence of json.dependencies) {
|
|
101
|
+
DEPENDENCIES.push(dependence);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const dev_dependence of json.dev_dependence) {
|
|
105
|
+
DEV_DEPENDENCIES.push(dev_dependence);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export { on_cancel, run_command, copy_template, copy_components };
|
package/src/lib/constants.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export const DEPENDENCIES = [
|
|
2
|
-
"@tanstack/react-query",
|
|
3
|
-
"axios",
|
|
4
|
-
"class-variance-authority",
|
|
5
|
-
"clsx",
|
|
6
|
-
"cookies-next",
|
|
7
|
-
"date-fns",
|
|
8
|
-
"lucide-react",
|
|
9
|
-
"next",
|
|
10
|
-
"next-auth",
|
|
11
|
-
"next-themes",
|
|
12
|
-
"react",
|
|
13
|
-
"react-dom",
|
|
14
|
-
"tailwind-merge",
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
export const DEV_DEPENDENCIES = [
|
|
18
|
-
"@eslint/eslintrc",
|
|
19
|
-
"@tailwindcss/postcss",
|
|
20
|
-
"@types/node",
|
|
21
|
-
"@types/react",
|
|
22
|
-
"@types/react-dom",
|
|
23
|
-
"eslint",
|
|
24
|
-
"eslint-config-next",
|
|
25
|
-
"tailwindcss",
|
|
26
|
-
"tw-animate-css",
|
|
27
|
-
"typescript",
|
|
28
|
-
];
|
package/src/lib/utils.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import prompts from "prompts";
|
|
5
|
-
import { execSync } from "node:child_process";
|
|
6
|
-
|
|
7
|
-
export async function get_prompts() {
|
|
8
|
-
const responses = await prompts([
|
|
9
|
-
{
|
|
10
|
-
type: "text",
|
|
11
|
-
name: "project_name",
|
|
12
|
-
message: "What is your project named?",
|
|
13
|
-
validate: (e) => {
|
|
14
|
-
if (e === undefined || e.trim() === "") {
|
|
15
|
-
return chalk.red("⚠️ The name is required");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (!/^[a-zA-Z0-9-_]+$/.test(e.trim())) {
|
|
19
|
-
return chalk.red(
|
|
20
|
-
"⚠️ Use only letters, numbers, dashes, and underscores."
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return true;
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
type: "select",
|
|
29
|
-
name: "package_manager",
|
|
30
|
-
message: "Which package manager would you want to use?",
|
|
31
|
-
choices: [
|
|
32
|
-
{ title: "npm", value: "npm" },
|
|
33
|
-
{ title: "pnpm", value: "pnpm" },
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
type: "multiselect",
|
|
38
|
-
name: "components",
|
|
39
|
-
message: "Which components do you want to install?",
|
|
40
|
-
instructions: false,
|
|
41
|
-
choices: [
|
|
42
|
-
{ title: "Accordion", value: "accordion" },
|
|
43
|
-
{ title: "Alert", value: "alert" },
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
onCancel: () => {
|
|
48
|
-
console.log(chalk.yellow("\n👋 Exiting..."));
|
|
49
|
-
process.exit(0);
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
]);
|
|
53
|
-
|
|
54
|
-
return responses;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function copy_template_files(src, project_path) {
|
|
58
|
-
try {
|
|
59
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
60
|
-
|
|
61
|
-
for (const entry of entries) {
|
|
62
|
-
const src_path = path.join(src, entry.name);
|
|
63
|
-
const dest_path = path.join(project_path, entry.name);
|
|
64
|
-
|
|
65
|
-
if (entry.isDirectory()) {
|
|
66
|
-
fs.mkdirSync(dest_path, { recursive: true });
|
|
67
|
-
copy_template_files(src_path, dest_path);
|
|
68
|
-
} else {
|
|
69
|
-
fs.copyFileSync(src_path, dest_path);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
} catch (error) {
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function copy_components_files(src, project_path, components) {
|
|
78
|
-
try {
|
|
79
|
-
components.forEach((e) => {
|
|
80
|
-
const component_path = path.resolve(src, `${e}.tsx`);
|
|
81
|
-
const dest_path = path.resolve(project_path, "src", "components", "ui");
|
|
82
|
-
|
|
83
|
-
if (fs.existsSync(component_path) === false) {
|
|
84
|
-
throw new Error(`Component ${e} not found`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (fs.existsSync(dest_path) === false) {
|
|
88
|
-
fs.mkdirSync(dest_path, { recursive: true });
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
fs.copyFileSync(component_path, path.join(dest_path, `${e}.tsx`));
|
|
92
|
-
});
|
|
93
|
-
} catch (error) {
|
|
94
|
-
throw error;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function copy_hooks_files(
|
|
99
|
-
src,
|
|
100
|
-
components_path,
|
|
101
|
-
project_path,
|
|
102
|
-
components
|
|
103
|
-
) {
|
|
104
|
-
const map = {};
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
components.forEach((e) => {
|
|
108
|
-
const hook = map[e];
|
|
109
|
-
const component_path = path.resolve(components_path, `${e}.tsx`);
|
|
110
|
-
|
|
111
|
-
if (fs.existsSync(component_path) === false) {
|
|
112
|
-
throw new Error(`Component ${e} not found`);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (hook === undefined) {
|
|
116
|
-
console.warn(`No hook mapping found for component ${e}, skipping.`);
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const hook_path = path.resolve(src, `${hook}.ts`);
|
|
121
|
-
const dest_path = path.resolve(project_path, "src", "hooks");
|
|
122
|
-
|
|
123
|
-
if (fs.existsSync(hook_path) === false) {
|
|
124
|
-
throw new Error(`Hook ${hook} not found for component ${e}`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (fs.existsSync(dest_path) === false) {
|
|
128
|
-
fs.mkdirSync(dest_path, { recursive: true });
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
fs.copyFileSync(hook_path, path.join(dest_path, `${hook}.ts`));
|
|
132
|
-
});
|
|
133
|
-
} catch (error) {
|
|
134
|
-
throw error;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function run(cmd, cwd) {
|
|
139
|
-
try {
|
|
140
|
-
execSync(cmd, { cwd, stdio: "inherit" });
|
|
141
|
-
} catch (error) {
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
["@radix-ui/react-accordion"]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
[]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|