create-fsd-architecture 1.0.1 → 1.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/.claude/settings.local.json +9 -0
- package/README.md +84 -0
- package/ashraf/README.md +122 -0
- package/ashraf/bun.lock +1347 -0
- package/ashraf/components.json +23 -0
- package/ashraf/eslint.config.js +23 -0
- package/ashraf/index.html +13 -0
- package/ashraf/package-lock.json +9886 -0
- package/ashraf/package.json +43 -0
- package/ashraf/src/app/App.tsx +12 -0
- package/ashraf/src/app/providers/axios-interceptor.tsx +0 -0
- package/ashraf/src/app/providers.tsx +0 -0
- package/ashraf/src/app/routing/auth-routes.tsx +0 -0
- package/ashraf/src/app/routing/dashboard-routes.tsx +0 -0
- package/ashraf/src/app/routing/guards/auth-guard.tsx +0 -0
- package/ashraf/src/app/routing/guards/dashboard-guard.tsx +0 -0
- package/ashraf/src/app/styles/index.css +190 -0
- package/ashraf/src/assets/index.ts +0 -0
- package/ashraf/src/entities/products/api/get-product-by-id.ts +8 -0
- package/ashraf/src/entities/products/api/get-products.ts +8 -0
- package/ashraf/src/entities/products/index.ts +5 -0
- package/ashraf/src/entities/products/model/query-keys.ts +5 -0
- package/ashraf/src/entities/products/model/types.ts +18 -0
- package/ashraf/src/entities/products/ui/product-card.tsx +25 -0
- package/ashraf/src/features/products/create-product/api/create-product.ts +7 -0
- package/ashraf/src/features/products/create-product/index.ts +2 -0
- package/ashraf/src/features/products/create-product/model/create-product-schema.ts +11 -0
- package/ashraf/src/features/products/create-product/model/use-create-product.ts +15 -0
- package/ashraf/src/features/products/create-product/ui/create-product-form.tsx +85 -0
- package/ashraf/src/features/products/update-product/api/update-product.ts +12 -0
- package/ashraf/src/features/products/update-product/index.ts +2 -0
- package/ashraf/src/features/products/update-product/model/update-product.schema.ts +11 -0
- package/ashraf/src/features/products/update-product/model/use-update-product.ts +17 -0
- package/ashraf/src/features/products/update-product/ui/update-product-form.tsx +69 -0
- package/ashraf/src/main.tsx +10 -0
- package/ashraf/src/pages/products/all-products.tsx +23 -0
- package/ashraf/src/pages/products/create-product-page.tsx +10 -0
- package/ashraf/src/pages/products/update-product-page.tsx +24 -0
- package/ashraf/src/shared/config/env.ts +1 -0
- package/ashraf/src/shared/lib/utils.ts +6 -0
- package/ashraf/src/shared/types/types.d.ts +0 -0
- package/ashraf/src/shared/ui/input.tsx +21 -0
- package/ashraf/src/shared/ui/label.tsx +22 -0
- package/ashraf/src/widgets/dashboard-header.tsx +0 -0
- package/ashraf/src/widgets/index.ts +0 -0
- package/ashraf/src/widgets/sidebar.tsx +0 -0
- package/ashraf/tsconfig.app.json +32 -0
- package/ashraf/tsconfig.json +21 -0
- package/ashraf/tsconfig.node.json +26 -0
- package/ashraf/vite.config.ts +14 -0
- package/bin/index.mjs +180 -39
- package/package.json +3 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/shared/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
6
|
+
return (
|
|
7
|
+
<input
|
|
8
|
+
type={type}
|
|
9
|
+
data-slot="input"
|
|
10
|
+
className={cn(
|
|
11
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
13
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { Input }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Label as LabelPrimitive } from "radix-ui"
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/shared/lib/utils"
|
|
5
|
+
|
|
6
|
+
function Label({
|
|
7
|
+
className,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
10
|
+
return (
|
|
11
|
+
<LabelPrimitive.Root
|
|
12
|
+
data-slot="label"
|
|
13
|
+
className={cn(
|
|
14
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { Label }
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"baseUrl": ".",
|
|
4
|
+
"paths": {
|
|
5
|
+
"@/*": ["./src/*"]
|
|
6
|
+
},
|
|
7
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
8
|
+
"target": "ES2022",
|
|
9
|
+
"useDefineForClassFields": true,
|
|
10
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
11
|
+
"module": "ESNext",
|
|
12
|
+
"types": ["vite/client"],
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
|
|
15
|
+
/* Bundler mode */
|
|
16
|
+
"moduleResolution": "bundler",
|
|
17
|
+
"allowImportingTsExtensions": true,
|
|
18
|
+
"verbatimModuleSyntax": true,
|
|
19
|
+
"moduleDetection": "force",
|
|
20
|
+
"noEmit": true,
|
|
21
|
+
"jsx": "react-jsx",
|
|
22
|
+
|
|
23
|
+
/* Linting */
|
|
24
|
+
"strict": true,
|
|
25
|
+
"noUnusedLocals": true,
|
|
26
|
+
"noUnusedParameters": true,
|
|
27
|
+
"erasableSyntaxOnly": true,
|
|
28
|
+
"noFallthroughCasesInSwitch": true,
|
|
29
|
+
"noUncheckedSideEffectImports": true
|
|
30
|
+
},
|
|
31
|
+
"include": ["src"]
|
|
32
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"strict": true,
|
|
3
|
+
"noUncheckedIndexedAccess": true,
|
|
4
|
+
"files": [],
|
|
5
|
+
"references": [
|
|
6
|
+
{
|
|
7
|
+
"path": "./tsconfig.app.json"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"path": "./tsconfig.node.json"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"compilerOptions": {
|
|
14
|
+
"baseUrl": ".",
|
|
15
|
+
"paths": {
|
|
16
|
+
"@/*": [
|
|
17
|
+
"./src/*"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "ES2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
"noUncheckedSideEffectImports": true
|
|
24
|
+
},
|
|
25
|
+
"include": ["vite.config.ts"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
import react from '@vitejs/plugin-react-swc';
|
|
4
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
5
|
+
|
|
6
|
+
// https://vite.dev/config/
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [react(), tailwindcss()],
|
|
9
|
+
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
"@": path.resolve(__dirname, "./src"),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
})
|
package/bin/index.mjs
CHANGED
|
@@ -3,58 +3,199 @@
|
|
|
3
3
|
import degit from "degit";
|
|
4
4
|
import prompts from "prompts";
|
|
5
5
|
import chalk from "chalk";
|
|
6
|
+
import ora from "ora";
|
|
6
7
|
import fs from "fs";
|
|
7
8
|
import path from "path";
|
|
8
|
-
import { execSync } from "child_process";
|
|
9
|
+
import { execSync, spawn } from "child_process";
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
+
const TEMPLATES = [
|
|
12
|
+
{
|
|
13
|
+
title: "React + Vite",
|
|
14
|
+
value: "react-vite",
|
|
15
|
+
description: "React with Vite and FSD architecture",
|
|
16
|
+
repo: "ashrafmo-1/FSD",
|
|
17
|
+
available: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
title: "Next.js",
|
|
21
|
+
value: "nextjs",
|
|
22
|
+
description: "Coming Soon",
|
|
23
|
+
repo: null,
|
|
24
|
+
available: false,
|
|
25
|
+
},
|
|
26
|
+
];
|
|
11
27
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
function showBanner() {
|
|
29
|
+
const line = chalk.cyan("─".repeat(39));
|
|
30
|
+
console.log();
|
|
31
|
+
console.log(` ${chalk.cyan("╭")}${line}${chalk.cyan("╮")}`);
|
|
32
|
+
console.log(` ${chalk.cyan("│")}${" ".repeat(39)}${chalk.cyan("│")}`);
|
|
33
|
+
console.log(` ${chalk.cyan("│")}${" ".repeat(7)}${chalk.bold.white("create-fsd-architecture")}${" ".repeat(9)}${chalk.cyan("│")}`);
|
|
34
|
+
console.log(` ${chalk.cyan("│")}${" ".repeat(3)}${chalk.dim("Feature-Sliced Design Scaffolding")}${" ".repeat(3)}${chalk.cyan("│")}`);
|
|
35
|
+
console.log(` ${chalk.cyan("│")}${" ".repeat(39)}${chalk.cyan("│")}`);
|
|
36
|
+
console.log(` ${chalk.cyan("╰")}${line}${chalk.cyan("╯")}`);
|
|
37
|
+
console.log();
|
|
38
|
+
}
|
|
22
39
|
|
|
23
|
-
|
|
40
|
+
function showNextSteps(projectName, depsInstalled) {
|
|
41
|
+
console.log();
|
|
42
|
+
console.log(chalk.bold.white(" Next steps:"));
|
|
43
|
+
console.log();
|
|
44
|
+
console.log(` ${chalk.cyan("$")} cd ${projectName}`);
|
|
45
|
+
if (!depsInstalled) {
|
|
46
|
+
console.log(` ${chalk.cyan("$")} npm install`);
|
|
47
|
+
}
|
|
48
|
+
console.log(` ${chalk.cyan("$")} npm run dev`);
|
|
49
|
+
console.log();
|
|
50
|
+
}
|
|
24
51
|
|
|
25
|
-
|
|
26
|
-
console.log(
|
|
27
|
-
|
|
52
|
+
function handleCancel() {
|
|
53
|
+
console.log();
|
|
54
|
+
console.log(chalk.yellow(" Cancelled."));
|
|
55
|
+
console.log();
|
|
56
|
+
process.exit(0);
|
|
28
57
|
}
|
|
29
58
|
|
|
30
|
-
const
|
|
59
|
+
const onCancel = { onCancel: handleCancel };
|
|
31
60
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
61
|
+
async function main() {
|
|
62
|
+
showBanner();
|
|
36
63
|
|
|
37
|
-
|
|
64
|
+
const argName = process.argv[2];
|
|
38
65
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
66
|
+
const { projectName } = argName
|
|
67
|
+
? { projectName: argName }
|
|
68
|
+
: await prompts(
|
|
69
|
+
{
|
|
70
|
+
type: "text",
|
|
71
|
+
name: "projectName",
|
|
72
|
+
message: "Project name",
|
|
73
|
+
initial: "my-fsd-app",
|
|
74
|
+
},
|
|
75
|
+
onCancel
|
|
76
|
+
);
|
|
44
77
|
|
|
45
|
-
|
|
78
|
+
if (!projectName) {
|
|
79
|
+
console.log(chalk.red(" Project name is required."));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
46
82
|
|
|
47
|
-
|
|
83
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
48
84
|
|
|
49
|
-
|
|
85
|
+
if (fs.existsSync(targetDir)) {
|
|
86
|
+
console.log(chalk.red(` Folder "${projectName}" already exists.`));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
50
89
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
90
|
+
const { template } = await prompts(
|
|
91
|
+
{
|
|
92
|
+
type: "select",
|
|
93
|
+
name: "template",
|
|
94
|
+
message: "Select project type",
|
|
95
|
+
choices: TEMPLATES.map((t) => ({
|
|
96
|
+
title: t.available
|
|
97
|
+
? t.title
|
|
98
|
+
: `${t.title} ${chalk.dim("(Coming Soon)")}`,
|
|
99
|
+
value: t.value,
|
|
100
|
+
})),
|
|
101
|
+
},
|
|
102
|
+
onCancel
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const selected = TEMPLATES.find((t) => t.value === template);
|
|
106
|
+
|
|
107
|
+
if (!selected || !selected.available) {
|
|
108
|
+
console.log();
|
|
109
|
+
console.log(
|
|
110
|
+
chalk.yellow(` ${selected ? selected.title : "Template"} support is coming soon!`)
|
|
111
|
+
);
|
|
112
|
+
console.log(chalk.dim(" Stay tuned for updates."));
|
|
113
|
+
console.log();
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log();
|
|
118
|
+
|
|
119
|
+
const spinner = ora({
|
|
120
|
+
text: "Downloading template...",
|
|
121
|
+
color: "cyan",
|
|
122
|
+
}).start();
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const emitter = degit(selected.repo, { cache: false, force: true });
|
|
126
|
+
await emitter.clone(targetDir);
|
|
127
|
+
spinner.succeed(chalk.green("Template downloaded."));
|
|
128
|
+
} catch (err) {
|
|
129
|
+
spinner.fail(chalk.red("Failed to download template."));
|
|
130
|
+
console.error(chalk.dim(` ${err.message}`));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
55
133
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
134
|
+
const { installDeps } = await prompts(
|
|
135
|
+
{
|
|
136
|
+
type: "confirm",
|
|
137
|
+
name: "installDeps",
|
|
138
|
+
message: "Install dependencies now?",
|
|
139
|
+
initial: true,
|
|
140
|
+
},
|
|
141
|
+
onCancel
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
let depsInstalled = false;
|
|
145
|
+
|
|
146
|
+
if (installDeps) {
|
|
147
|
+
const installSpinner = ora({
|
|
148
|
+
text: "Installing dependencies...",
|
|
149
|
+
color: "cyan",
|
|
150
|
+
}).start();
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
execSync("npm install", { cwd: targetDir, stdio: "pipe" });
|
|
154
|
+
installSpinner.succeed(chalk.green("Dependencies installed."));
|
|
155
|
+
depsInstalled = true;
|
|
156
|
+
} catch (err) {
|
|
157
|
+
installSpinner.fail(chalk.red("Failed to install dependencies."));
|
|
158
|
+
console.log(chalk.dim(" You can install them manually later."));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (depsInstalled) {
|
|
163
|
+
const { startDev } = await prompts(
|
|
164
|
+
{
|
|
165
|
+
type: "confirm",
|
|
166
|
+
name: "startDev",
|
|
167
|
+
message: "Start development server now?",
|
|
168
|
+
initial: true,
|
|
169
|
+
},
|
|
170
|
+
onCancel
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
if (startDev) {
|
|
174
|
+
console.log();
|
|
175
|
+
console.log(chalk.cyan(" Starting development server..."));
|
|
176
|
+
console.log();
|
|
177
|
+
|
|
178
|
+
const child = spawn("npm", ["run", "dev"], {
|
|
179
|
+
cwd: targetDir,
|
|
180
|
+
stdio: "inherit",
|
|
181
|
+
shell: true,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
child.on("error", (err) => {
|
|
185
|
+
console.log(chalk.red(` Failed to start dev server: ${err.message}`));
|
|
186
|
+
showNextSteps(projectName, true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
console.log();
|
|
194
|
+
console.log(chalk.green.bold(" Project created successfully!"));
|
|
195
|
+
showNextSteps(projectName, depsInstalled);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
main().catch((err) => {
|
|
199
|
+
console.error(chalk.red(` Error: ${err.message}`));
|
|
200
|
+
process.exit(1);
|
|
201
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-fsd-architecture",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"chalk": "^5.3.0",
|
|
15
|
+
"create-fsd-architecture": "^1.0.1",
|
|
15
16
|
"degit": "^2.8.4",
|
|
17
|
+
"ora": "^9.4.0",
|
|
16
18
|
"prompts": "^2.4.2"
|
|
17
19
|
},
|
|
18
20
|
"keywords": [],
|