@xanahlight/component-forge 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/LICENSE +21 -0
- package/README.md +266 -0
- package/README.ru.md +219 -0
- package/dist/commands/generate.d.ts +6 -0
- package/dist/commands/generate.js +91 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.js +54 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/validate.d.ts +1 -0
- package/dist/commands/validate.js +119 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/files.d.ts +8 -0
- package/dist/templates/files.js +112 -0
- package/dist/templates/files.js.map +1 -0
- package/dist/templates/fsd.d.ts +16 -0
- package/dist/templates/fsd.js +20 -0
- package/dist/templates/fsd.js.map +1 -0
- package/dist/templates/modular.d.ts +12 -0
- package/dist/templates/modular.js +16 -0
- package/dist/templates/modular.js.map +1 -0
- package/dist/types/folder-tree.d.ts +30 -0
- package/dist/types/folder-tree.js +3 -0
- package/dist/types/folder-tree.js.map +1 -0
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.js +33 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.js +14 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/template-resolver.d.ts +11 -0
- package/dist/utils/template-resolver.js +63 -0
- package/dist/utils/template-resolver.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vladislavprozorov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# component-forge
|
|
2
|
+
|
|
3
|
+
🌐 English | [Русский](README.ru.md)
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@xanahlight/component-forge)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
> Architecture-first CLI for scalable React projects.
|
|
10
|
+
|
|
11
|
+
A tool that enforces structural discipline through predefined architectural templates — Feature-Sliced Design (FSD) and Modular Architecture.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why
|
|
16
|
+
|
|
17
|
+
React gives you flexibility — but not structure.
|
|
18
|
+
|
|
19
|
+
In growing teams this often leads to:
|
|
20
|
+
|
|
21
|
+
- inconsistent folder structures across engineers
|
|
22
|
+
- unclear layer boundaries and ownership
|
|
23
|
+
- tight coupling between features
|
|
24
|
+
- architectural degradation over time
|
|
25
|
+
|
|
26
|
+
**component-forge** solves this by providing:
|
|
27
|
+
|
|
28
|
+
- standardized project structure from day one
|
|
29
|
+
- enforced architectural layers
|
|
30
|
+
- public API boundaries via `index.ts` files
|
|
31
|
+
- a `validate` command that catches violations before they ship
|
|
32
|
+
|
|
33
|
+
> This is not just a scaffolding tool. It is an **architecture enforcement tool**.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Target Audience
|
|
38
|
+
|
|
39
|
+
- React teams (3–10 developers)
|
|
40
|
+
- Growing startups with shared codebases
|
|
41
|
+
- Developers who have experienced architectural chaos
|
|
42
|
+
- Engineers who value structure and long-term maintainability
|
|
43
|
+
|
|
44
|
+
> Not designed for beginners learning React basics.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Supported Architectures
|
|
49
|
+
|
|
50
|
+
### 1. Feature-Sliced Design (FSD)
|
|
51
|
+
|
|
52
|
+
Generates a layered structure:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
src/
|
|
56
|
+
├─ app/
|
|
57
|
+
├─ processes/
|
|
58
|
+
├─ pages/
|
|
59
|
+
├─ widgets/
|
|
60
|
+
├─ features/
|
|
61
|
+
├─ entities/
|
|
62
|
+
└─ shared/
|
|
63
|
+
├─ ui/
|
|
64
|
+
├─ lib/
|
|
65
|
+
├─ api/
|
|
66
|
+
└─ config/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Enforces strict layer hierarchy — app → pages → widgets → features → entities → shared.
|
|
70
|
+
No deep imports. Public API only via `index.ts`.
|
|
71
|
+
|
|
72
|
+
### 2. Modular Architecture
|
|
73
|
+
|
|
74
|
+
Generates a domain-oriented structure:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
src/
|
|
78
|
+
├─ modules/
|
|
79
|
+
│ ├─ auth/
|
|
80
|
+
│ ├─ profile/
|
|
81
|
+
│ └─ dashboard/
|
|
82
|
+
├─ shared/
|
|
83
|
+
└─ core/
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Focused on domain separation and scalable team ownership.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Global install
|
|
94
|
+
npm install -g @xanahlight/component-forge
|
|
95
|
+
|
|
96
|
+
# No install required (recommended for one-time setup)
|
|
97
|
+
npx @xanahlight/component-forge init fsd
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Commands
|
|
103
|
+
|
|
104
|
+
### init
|
|
105
|
+
|
|
106
|
+
Creates the folder structure and writes `.component-forge.json`.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
component-forge init fsd
|
|
110
|
+
component-forge init modular
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### generate
|
|
114
|
+
|
|
115
|
+
Generates a slice with pre-populated files.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# FSD slices
|
|
119
|
+
component-forge generate feature auth
|
|
120
|
+
component-forge generate entity user
|
|
121
|
+
component-forge generate widget Header
|
|
122
|
+
component-forge generate page dashboard
|
|
123
|
+
|
|
124
|
+
# Components (placed in shared/ui)
|
|
125
|
+
component-forge generate component Button
|
|
126
|
+
component-forge generate component forms/Input
|
|
127
|
+
|
|
128
|
+
# Modular
|
|
129
|
+
component-forge generate module profile
|
|
130
|
+
|
|
131
|
+
# Short alias
|
|
132
|
+
component-forge g feature auth
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
What gets generated for `generate feature auth`:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
src/features/auth/
|
|
139
|
+
├─ index.ts <- public API
|
|
140
|
+
├─ ui/Auth.tsx <- React component
|
|
141
|
+
├─ model/index.ts <- state / hooks
|
|
142
|
+
└─ api/index.ts <- data fetching
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### validate
|
|
146
|
+
|
|
147
|
+
Validates your project structure against the configured architecture.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
component-forge validate
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Checks:
|
|
154
|
+
|
|
155
|
+
- Required layers are present (error)
|
|
156
|
+
- Unknown layers that do not belong to the architecture (warning)
|
|
157
|
+
- Slices missing a public API `index.ts` (warning)
|
|
158
|
+
|
|
159
|
+
Exits with code `1` on errors — suitable for CI pipelines.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Project Config
|
|
164
|
+
|
|
165
|
+
After `init`, a `.component-forge.json` is created:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"architecture": "fsd",
|
|
170
|
+
"srcDir": "src"
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
All commands read this config automatically. No flags needed after init.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Custom Templates
|
|
179
|
+
|
|
180
|
+
You can override any built-in file template with your own [Handlebars](https://handlebarsjs.com/) (`.hbs`) files.
|
|
181
|
+
|
|
182
|
+
**1. Add a `templates` field to `.component-forge.json`:**
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"architecture": "fsd",
|
|
187
|
+
"srcDir": "src",
|
|
188
|
+
"templates": ".forge-templates"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**2. Create `.hbs` files mirroring the built-in structure:**
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
.forge-templates/
|
|
196
|
+
└─ feature/
|
|
197
|
+
└─ index.ts.hbs ← overrides built-in index.ts for features
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Available template variables:**
|
|
201
|
+
|
|
202
|
+
| Variable | Description | Example |
|
|
203
|
+
| --- | --- | --- |
|
|
204
|
+
| `{{name}}` | Raw slice name | `auth` |
|
|
205
|
+
| `{{Name}}` | PascalCase name | `Auth` |
|
|
206
|
+
| `{{sliceType}}` | Slice type | `feature` |
|
|
207
|
+
|
|
208
|
+
**Example `.forge-templates/feature/index.ts.hbs`:**
|
|
209
|
+
|
|
210
|
+
```handlebars
|
|
211
|
+
// {{sliceType}} public API
|
|
212
|
+
export { {{Name}} } from './ui/{{Name}}'
|
|
213
|
+
export type { {{Name}}Props } from './ui/{{Name}}'
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Any template file not found in your custom directory automatically falls back to the built-in default.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Philosophy
|
|
221
|
+
|
|
222
|
+
- **Opinionated > Flexible** — strong defaults prevent decision fatigue
|
|
223
|
+
- **Structure > Freedom** — constraints enable scale
|
|
224
|
+
- **Predictability > Improvisation** — anyone on the team knows where things live
|
|
225
|
+
- **Enforcement > Convention** — `validate` catches drift before it becomes debt
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Development
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
git clone https://github.com/vladislavprozorov/component-forge.git
|
|
233
|
+
cd component-forge
|
|
234
|
+
npm install
|
|
235
|
+
npm run build
|
|
236
|
+
node dist/index.js init fsd
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
> Node.js 20+ required.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Project Status
|
|
244
|
+
|
|
245
|
+
Active development. Core commands are functional. API may change before 1.0.
|
|
246
|
+
|
|
247
|
+
- [x] `init` — FSD and Modular scaffolding
|
|
248
|
+
- [x] `generate` — slices with file templates
|
|
249
|
+
- [x] `validate` — architecture enforcement
|
|
250
|
+
- [x] `generate --dry-run` — preview without writing
|
|
251
|
+
- [x] Custom templates via config
|
|
252
|
+
- [ ] VS Code extension
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Contributing
|
|
257
|
+
|
|
258
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
259
|
+
|
|
260
|
+
Please open an issue before submitting large changes.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
[MIT](LICENSE)
|
package/README.ru.md
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# component-forge
|
|
2
|
+
|
|
3
|
+
🌐 [English](README.md) | Русский
|
|
4
|
+
|
|
5
|
+
> CLI инструмент для масштабируемых React проектов с акцентом на архитектуру.
|
|
6
|
+
|
|
7
|
+
Инструмент, обеспечивающий структурную дисциплину через готовые архитектурные шаблоны — Feature-Sliced Design (FSD) и Модульную архитектуру.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Зачем это нужно
|
|
12
|
+
|
|
13
|
+
React даёт гибкость — но не структуру.
|
|
14
|
+
|
|
15
|
+
В растущих командах это часто приводит к:
|
|
16
|
+
|
|
17
|
+
- непоследовательным структурам папок
|
|
18
|
+
- размытым границам между слоями
|
|
19
|
+
- тесной связанности между фичами
|
|
20
|
+
- архитектурной деградации со временем
|
|
21
|
+
|
|
22
|
+
**component-forge** решает это:
|
|
23
|
+
|
|
24
|
+
- стандартизированная структура проекта с первого дня
|
|
25
|
+
- чёткие архитектурные слои
|
|
26
|
+
- публичные API через файлы `index.ts`
|
|
27
|
+
- команда `validate` ловит нарушения до попадания в прод
|
|
28
|
+
|
|
29
|
+
> Это не просто инструмент генерации файлов. Это **инструмент контроля архитектуры**.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Целевая аудитория
|
|
34
|
+
|
|
35
|
+
- React команды (3–10 разработчиков)
|
|
36
|
+
- Растущие стартапы с общей кодовой базой
|
|
37
|
+
- Разработчики, уже сталкивавшиеся с архитектурным хаосом
|
|
38
|
+
- Инженеры, ценящие структуру и долгосрочную поддерживаемость
|
|
39
|
+
|
|
40
|
+
> Не предназначен для новичков, изучающих основы React.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Поддерживаемые архитектуры
|
|
45
|
+
|
|
46
|
+
### 1. Feature-Sliced Design (FSD)
|
|
47
|
+
|
|
48
|
+
Генерирует слоистую структуру:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
src/
|
|
52
|
+
├─ app/
|
|
53
|
+
├─ processes/
|
|
54
|
+
├─ pages/
|
|
55
|
+
├─ widgets/
|
|
56
|
+
├─ features/
|
|
57
|
+
├─ entities/
|
|
58
|
+
└─ shared/
|
|
59
|
+
├─ ui/
|
|
60
|
+
├─ lib/
|
|
61
|
+
├─ api/
|
|
62
|
+
└─ config/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Обеспечивает строгую иерархию слоёв — app → pages → widgets → features → entities → shared.
|
|
66
|
+
Нет глубоких импортов. Только публичный API через `index.ts`.
|
|
67
|
+
|
|
68
|
+
### 2. Модульная архитектура
|
|
69
|
+
|
|
70
|
+
Генерирует доменно-ориентированную структуру:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
src/
|
|
74
|
+
├─ modules/
|
|
75
|
+
│ ├─ auth/
|
|
76
|
+
│ ├─ profile/
|
|
77
|
+
│ └─ dashboard/
|
|
78
|
+
├─ shared/
|
|
79
|
+
└─ core/
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Фокус на разделении по доменам и масштабируемом владении кодом.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Установка
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Глобальная установка
|
|
90
|
+
npm install -g component-forge
|
|
91
|
+
|
|
92
|
+
# Без установки (рекомендуется для разового использования)
|
|
93
|
+
npx component-forge init fsd
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Команды
|
|
99
|
+
|
|
100
|
+
### init
|
|
101
|
+
|
|
102
|
+
Создаёт структуру папок и записывает `.component-forge.json`.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
component-forge init fsd
|
|
106
|
+
component-forge init modular
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### generate
|
|
110
|
+
|
|
111
|
+
Генерирует слайс с готовыми файлами.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# FSD слайсы
|
|
115
|
+
component-forge generate feature auth
|
|
116
|
+
component-forge generate entity user
|
|
117
|
+
component-forge generate widget Header
|
|
118
|
+
component-forge generate page dashboard
|
|
119
|
+
|
|
120
|
+
# Компоненты (размещаются в shared/ui)
|
|
121
|
+
component-forge generate component Button
|
|
122
|
+
component-forge generate component forms/Input
|
|
123
|
+
|
|
124
|
+
# Модульная архитектура
|
|
125
|
+
component-forge generate module profile
|
|
126
|
+
|
|
127
|
+
# Короткий алиас
|
|
128
|
+
component-forge g feature auth
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Что генерируется для `generate feature auth`:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
src/features/auth/
|
|
135
|
+
├─ index.ts <- публичный API
|
|
136
|
+
├─ ui/Auth.tsx <- React компонент
|
|
137
|
+
├─ model/index.ts <- состояние / хуки
|
|
138
|
+
└─ api/index.ts <- запросы к данным
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### validate
|
|
142
|
+
|
|
143
|
+
Проверяет структуру проекта на соответствие настроенной архитектуре.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
component-forge validate
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Проверяет:
|
|
150
|
+
- Обязательные слои присутствуют (ошибка)
|
|
151
|
+
- Неизвестные слои, не относящиеся к архитектуре (предупреждение)
|
|
152
|
+
- Слайсы без публичного API `index.ts` (предупреждение)
|
|
153
|
+
|
|
154
|
+
Завершается с кодом `1` при ошибках — подходит для CI.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Конфигурация проекта
|
|
159
|
+
|
|
160
|
+
После `init` создаётся файл `.component-forge.json`:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"architecture": "fsd",
|
|
165
|
+
"srcDir": "src"
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Все команды читают этот конфиг автоматически. Флаги после init не нужны.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Философия
|
|
174
|
+
|
|
175
|
+
- **Мнение > Гибкость** — строгие настройки по умолчанию исключают лишние решения
|
|
176
|
+
- **Структура > Свобода** — ограничения обеспечивают масштабирование
|
|
177
|
+
- **Предсказуемость > Импровизация** — каждый в команде знает где что лежит
|
|
178
|
+
- **Контроль > Соглашение** — `validate` ловит отклонения до того как они становятся долгом
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Разработка
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
git clone https://github.com/vladislavprozorov/component-forge.git
|
|
186
|
+
cd component-forge
|
|
187
|
+
npm install
|
|
188
|
+
npm run build
|
|
189
|
+
node dist/index.js init fsd
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
> Требуется Node.js 20+.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Статус проекта
|
|
197
|
+
|
|
198
|
+
Активная разработка. Основные команды работают. API может измениться до версии 1.0.
|
|
199
|
+
|
|
200
|
+
- [x] `init` — скаффолдинг FSD и Modular
|
|
201
|
+
- [x] `generate` — слайсы с шаблонами файлов
|
|
202
|
+
- [x] `validate` — контроль архитектуры
|
|
203
|
+
- [ ] Флаг `generate --dry-run`
|
|
204
|
+
- [ ] Кастомные шаблоны через конфиг
|
|
205
|
+
- [ ] Расширение для VS Code
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Участие в проекте
|
|
210
|
+
|
|
211
|
+
См. [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
212
|
+
|
|
213
|
+
Пожалуйста, открой issue перед отправкой крупных изменений.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Лицензия
|
|
218
|
+
|
|
219
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,91 @@
|
|
|
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.generateCommand = generateCommand;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const config_1 = require("../utils/config");
|
|
10
|
+
const logger_1 = require("../utils/logger");
|
|
11
|
+
const template_resolver_1 = require("../utils/template-resolver");
|
|
12
|
+
const placementByArchitecture = {
|
|
13
|
+
fsd: {
|
|
14
|
+
feature: 'features',
|
|
15
|
+
entity: 'entities',
|
|
16
|
+
widget: 'widgets',
|
|
17
|
+
page: 'pages',
|
|
18
|
+
component: 'shared/ui',
|
|
19
|
+
},
|
|
20
|
+
modular: {
|
|
21
|
+
module: 'modules',
|
|
22
|
+
component: 'shared/ui',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Path resolution
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Resolves the absolute path for the slice being generated.
|
|
30
|
+
* Supports nested names like "forms/Input" → src/shared/ui/forms/Input
|
|
31
|
+
*/
|
|
32
|
+
function resolveSlicePath(config, sliceType, sliceName) {
|
|
33
|
+
const placement = placementByArchitecture[config.architecture][sliceType];
|
|
34
|
+
if (!placement) {
|
|
35
|
+
const available = Object.keys(placementByArchitecture[config.architecture]).join(', ');
|
|
36
|
+
logger_1.logger.error(`Slice type "${sliceType}" is not supported for ${config.architecture} architecture.`);
|
|
37
|
+
logger_1.logger.info(`Available types for ${config.architecture}: ${available}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
return node_path_1.default.join(process.cwd(), config.srcDir, placement, sliceName);
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// File I/O
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
function writeFile(filePath, content) {
|
|
46
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.dirname(filePath));
|
|
47
|
+
fs_extra_1.default.writeFileSync(filePath, content);
|
|
48
|
+
logger_1.logger.success(`Created: ${node_path_1.default.relative(process.cwd(), filePath)}`);
|
|
49
|
+
}
|
|
50
|
+
function printDryRun(filePath) {
|
|
51
|
+
logger_1.logger.info(`Would create: ${node_path_1.default.relative(process.cwd(), filePath)}`);
|
|
52
|
+
}
|
|
53
|
+
function generateCommand(sliceType, sliceName, options = {}) {
|
|
54
|
+
const { dryRun = false } = options;
|
|
55
|
+
const config = (0, config_1.loadProjectConfig)();
|
|
56
|
+
const slicePath = resolveSlicePath(config, sliceType, sliceName);
|
|
57
|
+
if (!dryRun && fs_extra_1.default.existsSync(slicePath)) {
|
|
58
|
+
logger_1.logger.error(`Already exists: ${node_path_1.default.relative(process.cwd(), slicePath)}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
// Resolve templates directory (absolute path) if configured
|
|
62
|
+
const templatesDir = config.templates
|
|
63
|
+
? node_path_1.default.resolve(process.cwd(), config.templates)
|
|
64
|
+
: undefined;
|
|
65
|
+
// Derive the bare name for use in templates (e.g. "forms/Input" → "Input")
|
|
66
|
+
const sliceBaseName = node_path_1.default.basename(sliceName);
|
|
67
|
+
const files = (0, template_resolver_1.resolveSliceFiles)(sliceType, sliceBaseName, templatesDir);
|
|
68
|
+
if (dryRun) {
|
|
69
|
+
logger_1.logger.info(`Dry run — no files will be written.\n`);
|
|
70
|
+
printDryRun(slicePath + '/');
|
|
71
|
+
for (const relativePath of Object.keys(files)) {
|
|
72
|
+
printDryRun(node_path_1.default.join(slicePath, relativePath));
|
|
73
|
+
}
|
|
74
|
+
logger_1.logger.info(`\nDry run complete. Run without --dry-run to generate.`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Create slice root
|
|
78
|
+
fs_extra_1.default.ensureDirSync(slicePath);
|
|
79
|
+
logger_1.logger.success(`Created: ${node_path_1.default.relative(process.cwd(), slicePath)}`);
|
|
80
|
+
// Write files from templates
|
|
81
|
+
for (const [relativePath, content] of Object.entries(files)) {
|
|
82
|
+
writeFile(node_path_1.default.join(slicePath, relativePath), content);
|
|
83
|
+
}
|
|
84
|
+
if (templatesDir) {
|
|
85
|
+
logger_1.logger.info(`Generated ${sliceType} "${sliceName}" successfully (custom templates).`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
logger_1.logger.info(`Generated ${sliceType} "${sliceName}" successfully.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Architecture } from '../types/folder-tree';
|
|
2
|
+
import { CONFIG_FILENAME, loadProjectConfig } from '../utils/config';
|
|
3
|
+
export { Architecture };
|
|
4
|
+
export { CONFIG_FILENAME };
|
|
5
|
+
/**
|
|
6
|
+
* Initialises the project folder structure for the given architecture
|
|
7
|
+
* and writes .component-forge.json so subsequent commands know the architecture.
|
|
8
|
+
*/
|
|
9
|
+
export declare function initCommand(architecture: Architecture): void;
|
|
10
|
+
export { loadProjectConfig };
|
|
@@ -0,0 +1,54 @@
|
|
|
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.loadProjectConfig = exports.CONFIG_FILENAME = void 0;
|
|
7
|
+
exports.initCommand = initCommand;
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const fsd_1 = require("../templates/fsd");
|
|
11
|
+
const modular_1 = require("../templates/modular");
|
|
12
|
+
const config_1 = require("../utils/config");
|
|
13
|
+
Object.defineProperty(exports, "CONFIG_FILENAME", { enumerable: true, get: function () { return config_1.CONFIG_FILENAME; } });
|
|
14
|
+
Object.defineProperty(exports, "loadProjectConfig", { enumerable: true, get: function () { return config_1.loadProjectConfig; } });
|
|
15
|
+
const logger_1 = require("../utils/logger");
|
|
16
|
+
/**
|
|
17
|
+
* Template registry — maps architecture → folder tree definition
|
|
18
|
+
*/
|
|
19
|
+
const templates = {
|
|
20
|
+
fsd: fsd_1.fsdTemplate,
|
|
21
|
+
modular: modular_1.modularTemplate,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Recursively creates folder structure from a FolderTree definition
|
|
25
|
+
*/
|
|
26
|
+
function createStructure(tree, basePath) {
|
|
27
|
+
for (const [folderName, children] of Object.entries(tree)) {
|
|
28
|
+
const folderPath = node_path_1.default.join(basePath, folderName);
|
|
29
|
+
fs_extra_1.default.ensureDirSync(folderPath);
|
|
30
|
+
logger_1.logger.success(`Created: ${node_path_1.default.relative(process.cwd(), folderPath)}`);
|
|
31
|
+
createStructure(children, folderPath);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Initialises the project folder structure for the given architecture
|
|
36
|
+
* and writes .component-forge.json so subsequent commands know the architecture.
|
|
37
|
+
*/
|
|
38
|
+
function initCommand(architecture) {
|
|
39
|
+
const template = templates[architecture];
|
|
40
|
+
const projectRoot = process.cwd();
|
|
41
|
+
const configPath = node_path_1.default.join(projectRoot, config_1.CONFIG_FILENAME);
|
|
42
|
+
if (fs_extra_1.default.existsSync(configPath)) {
|
|
43
|
+
logger_1.logger.error(`Project already initialised (${config_1.CONFIG_FILENAME} exists).`);
|
|
44
|
+
logger_1.logger.info('Remove it manually if you want to reinitialise.');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
logger_1.logger.info(`Initialising ${architecture.toUpperCase()} architecture…`);
|
|
48
|
+
createStructure(template, projectRoot);
|
|
49
|
+
const config = { architecture, srcDir: 'src' };
|
|
50
|
+
(0, config_1.writeProjectConfig)(config, projectRoot);
|
|
51
|
+
logger_1.logger.success(`Created: ${config_1.CONFIG_FILENAME}`);
|
|
52
|
+
logger_1.logger.success('Project structure successfully created.');
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":";;;;;;AAyCA,kCAoBC;AA7DD,wDAAyB;AACzB,0DAA4B;AAE5B,0CAA8C;AAC9C,kDAAsD;AAEtD,4CAAwF;AAO/E,gGAPA,wBAAe,OAOA;AAmDf,kGA1DiB,0BAAiB,OA0DjB;AAzD1B,4CAAwC;AAQxC;;GAEG;AACH,MAAM,SAAS,GAAqC;IAClD,GAAG,EAAE,iBAAW;IAChB,OAAO,EAAE,yBAAe;CACzB,CAAA;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,IAAgB,EAAE,QAAgB;IACzD,KAAK,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,MAAM,UAAU,GAAG,mBAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;QAElD,kBAAE,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;QAC5B,eAAM,CAAC,OAAO,CAAC,YAAY,mBAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;QAEtE,eAAe,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;IACvC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,YAA0B;IACpD,MAAM,QAAQ,GAAG,SAAS,CAAC,YAAY,CAAC,CAAA;IACxC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IACjC,MAAM,UAAU,GAAG,mBAAI,CAAC,IAAI,CAAC,WAAW,EAAE,wBAAe,CAAC,CAAA;IAE1D,IAAI,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,eAAM,CAAC,KAAK,CAAC,gCAAgC,wBAAe,WAAW,CAAC,CAAA;QACxE,eAAM,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAA;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,eAAM,CAAC,IAAI,CAAC,gBAAgB,YAAY,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAA;IAEvE,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA;IAEtC,MAAM,MAAM,GAAkB,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;IAC7D,IAAA,2BAAkB,EAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IACvC,eAAM,CAAC,OAAO,CAAC,YAAY,wBAAe,EAAE,CAAC,CAAA;IAE7C,eAAM,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAA;AAC3D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function validateCommand(): void;
|