create-next-pro-cli 0.1.5 → 0.1.6
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 +5 -1
- package/src/index.ts +10 -454
- package/src/lib/addComponent.ts +113 -0
- package/src/lib/addPage.ts +170 -0
- package/src/lib/createProject.ts +18 -0
- package/src/lib/createProjectWithPrompt.ts +79 -0
- package/src/lib/rmPage.ts +52 -0
- package/src/lib/utils.ts +36 -0
- package/src/save-index.ts +520 -0
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-next-pro-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Advanced Next.js project scaffolder with i18n, Tailwind, App Router and more.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/Rising-Corporation/create-next-pro-cli.git"
|
|
8
|
+
},
|
|
5
9
|
"bin": {
|
|
6
10
|
"create-next-pro": "./bin.ts"
|
|
7
11
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { addComponent } from "./lib/addComponent";
|
|
4
|
+
import { addPage } from "./lib/addPage";
|
|
5
|
+
import { rmPage } from "./lib/rmPage";
|
|
6
|
+
import { createProject } from "./lib/createProject";
|
|
7
|
+
import { createProjectWithPrompt } from "./lib/createProjectWithPrompt";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Main CLI entry point for create-next-pro.
|
|
@@ -30,115 +30,7 @@ export async function main() {
|
|
|
30
30
|
* Handle addcomponent command: create a component in the correct location and update translation JSON.
|
|
31
31
|
*/
|
|
32
32
|
if (args[0] === "addcomponent") {
|
|
33
|
-
|
|
34
|
-
let pageScope = null;
|
|
35
|
-
let pageIndex = args.findIndex((arg) => arg === "-P" || arg === "--page");
|
|
36
|
-
if (pageIndex !== -1 && args[pageIndex + 1]) {
|
|
37
|
-
pageScope = args[pageIndex + 1];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Handle nested pageScope (e.g. ParentPage.ChildPage)
|
|
41
|
-
let nestedPath = null;
|
|
42
|
-
if (pageScope && pageScope.includes(".")) {
|
|
43
|
-
nestedPath = join(...pageScope.split("."));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (!componentName || componentName.startsWith("-")) {
|
|
47
|
-
// Si le nom n'est pas fourni ou est une option, demander via prompt
|
|
48
|
-
const response = await prompts.prompt({
|
|
49
|
-
type: "text",
|
|
50
|
-
name: "componentName",
|
|
51
|
-
message: "🧩 Component name to add:",
|
|
52
|
-
validate: (name: string) =>
|
|
53
|
-
name ? true : "Component name is required",
|
|
54
|
-
});
|
|
55
|
-
componentName = response.componentName;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const componentNameUpper = capitalize(componentName);
|
|
59
|
-
const templatePath = join(import.meta.dir, "..", "templates", "Component");
|
|
60
|
-
const messagesPath = join(process.cwd(), "messages");
|
|
61
|
-
|
|
62
|
-
// Determine target path for the component
|
|
63
|
-
let componentTargetPath;
|
|
64
|
-
let translationKey;
|
|
65
|
-
if (pageScope) {
|
|
66
|
-
if (nestedPath) {
|
|
67
|
-
componentTargetPath = join(process.cwd(), "src", "ui", nestedPath);
|
|
68
|
-
translationKey = pageScope;
|
|
69
|
-
} else {
|
|
70
|
-
componentTargetPath = join(process.cwd(), "src", "ui", pageScope);
|
|
71
|
-
translationKey = pageScope;
|
|
72
|
-
}
|
|
73
|
-
} else {
|
|
74
|
-
componentTargetPath = join(process.cwd(), "src", "ui", "_global");
|
|
75
|
-
translationKey = "_global_ui";
|
|
76
|
-
}
|
|
77
|
-
if (!existsSync(componentTargetPath)) {
|
|
78
|
-
await mkdir(componentTargetPath, { recursive: true });
|
|
79
|
-
}
|
|
80
|
-
const componentFile = join(
|
|
81
|
-
componentTargetPath,
|
|
82
|
-
`${componentNameUpper}.tsx`
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
// Read and adapt the TSX template
|
|
86
|
-
const templateComponentPath = join(templatePath, "Component.tsx");
|
|
87
|
-
if (existsSync(templateComponentPath)) {
|
|
88
|
-
let content = await readFile(templateComponentPath, "utf-8");
|
|
89
|
-
// Remplacement du nom du component et de la clé de traduction
|
|
90
|
-
content = content
|
|
91
|
-
.replace(/Component/g, componentNameUpper)
|
|
92
|
-
.replace(/componentPage/g, translationKey);
|
|
93
|
-
await writeFile(componentFile, content);
|
|
94
|
-
console.log(`📄 File created: ${componentFile}`);
|
|
95
|
-
} else {
|
|
96
|
-
console.error(
|
|
97
|
-
"❌ Template Component.tsx introuvable :",
|
|
98
|
-
templateComponentPath
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Add the component to each language's JSON file (only folders)
|
|
103
|
-
const entries = await readdir(messagesPath, { withFileTypes: true });
|
|
104
|
-
const langDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
105
|
-
const jsonTemplate = join(templatePath, "component.json");
|
|
106
|
-
if (!existsSync(jsonTemplate)) {
|
|
107
|
-
console.error("❌ Template component.json not found:", jsonTemplate);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const jsonContent = await readFile(jsonTemplate, "utf-8");
|
|
111
|
-
const parsed = JSON.parse(jsonContent);
|
|
112
|
-
|
|
113
|
-
for (const locale of langDirs) {
|
|
114
|
-
// Only process if messages/<locale> is a directory
|
|
115
|
-
const localeDir = join(messagesPath, locale);
|
|
116
|
-
if (
|
|
117
|
-
!existsSync(localeDir) ||
|
|
118
|
-
!require("node:fs").statSync(localeDir).isDirectory()
|
|
119
|
-
)
|
|
120
|
-
continue;
|
|
121
|
-
let jsonTarget;
|
|
122
|
-
if (pageScope) {
|
|
123
|
-
jsonTarget = join(messagesPath, locale, `${pageScope}.json`);
|
|
124
|
-
} else {
|
|
125
|
-
jsonTarget = join(messagesPath, locale, `_global_ui.json`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
let current: Record<string, any> = {};
|
|
129
|
-
if (existsSync(jsonTarget)) {
|
|
130
|
-
const jsonFile = await readFile(jsonTarget, "utf-8");
|
|
131
|
-
current = JSON.parse(jsonFile) as Record<string, any>;
|
|
132
|
-
}
|
|
133
|
-
current[componentNameUpper] = parsed;
|
|
134
|
-
await writeFile(jsonTarget, JSON.stringify(current, null, 2));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
console.log(
|
|
138
|
-
`✅ Component "${componentNameUpper}" added ${
|
|
139
|
-
pageScope ? `to page ${pageScope}` : "globally"
|
|
140
|
-
} with localized messages.`
|
|
141
|
-
);
|
|
33
|
+
addComponent(args);
|
|
142
34
|
return;
|
|
143
35
|
}
|
|
144
36
|
|
|
@@ -146,168 +38,7 @@ export async function main() {
|
|
|
146
38
|
* Handle addpage command: create a page (nested or not) and update translation JSON.
|
|
147
39
|
*/
|
|
148
40
|
if (args[0] === "addpage") {
|
|
149
|
-
|
|
150
|
-
if (!pageName || pageName.startsWith("-")) {
|
|
151
|
-
const response = await prompts.prompt({
|
|
152
|
-
type: "text",
|
|
153
|
-
name: "pageName",
|
|
154
|
-
message: "📝 Page name to add:",
|
|
155
|
-
validate: (name: string) => (name ? true : "Page name is required"),
|
|
156
|
-
});
|
|
157
|
-
pageName = response.pageName;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Handle nested pages
|
|
161
|
-
let parentName = null;
|
|
162
|
-
let childName = null;
|
|
163
|
-
if (pageName.includes(".")) {
|
|
164
|
-
[parentName, childName] = pageName.split(".");
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
let shortFlags = args.find((arg) => /^-[A-Za-z]+$/.test(arg));
|
|
168
|
-
let longFlags = new Set(args.filter((a) => a.startsWith("--")));
|
|
169
|
-
const flags = new Set<string>();
|
|
170
|
-
|
|
171
|
-
if (!shortFlags && Array.from(longFlags).length === 0) {
|
|
172
|
-
shortFlags = "-LPl";
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (shortFlags) {
|
|
176
|
-
for (const char of shortFlags.slice(1)) {
|
|
177
|
-
switch (char) {
|
|
178
|
-
case "L":
|
|
179
|
-
flags.add("layout");
|
|
180
|
-
break;
|
|
181
|
-
case "P":
|
|
182
|
-
flags.add("page");
|
|
183
|
-
break;
|
|
184
|
-
case "l":
|
|
185
|
-
flags.add("loading");
|
|
186
|
-
break;
|
|
187
|
-
case "n":
|
|
188
|
-
flags.add("not-found");
|
|
189
|
-
break;
|
|
190
|
-
case "e":
|
|
191
|
-
flags.add("error");
|
|
192
|
-
break;
|
|
193
|
-
case "g":
|
|
194
|
-
flags.add("global-error");
|
|
195
|
-
break;
|
|
196
|
-
case "r":
|
|
197
|
-
flags.add("route");
|
|
198
|
-
break;
|
|
199
|
-
case "t":
|
|
200
|
-
flags.add("template");
|
|
201
|
-
break;
|
|
202
|
-
case "d":
|
|
203
|
-
flags.add("default");
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
for (const flag of [
|
|
210
|
-
"layout",
|
|
211
|
-
"page",
|
|
212
|
-
"loading",
|
|
213
|
-
"not-found",
|
|
214
|
-
"error",
|
|
215
|
-
"global-error",
|
|
216
|
-
"route",
|
|
217
|
-
"template",
|
|
218
|
-
"default",
|
|
219
|
-
]) {
|
|
220
|
-
if (longFlags.has("--" + flag)) flags.add(flag);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const srcPath = join(process.cwd(), "src", "app", "[locale]");
|
|
224
|
-
const messagesPath = join(process.cwd(), "messages");
|
|
225
|
-
const templatePath = join(import.meta.dir, "..", "templates", "Page");
|
|
226
|
-
const entries = await readdir(messagesPath, { withFileTypes: true });
|
|
227
|
-
const locales = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
228
|
-
|
|
229
|
-
// Create folders/files for nested or simple page
|
|
230
|
-
let uiPageDir, localePagePath, jsonFileName;
|
|
231
|
-
if (parentName && childName) {
|
|
232
|
-
uiPageDir = join(process.cwd(), "src", "ui", parentName, childName);
|
|
233
|
-
localePagePath = join(srcPath, parentName, childName);
|
|
234
|
-
jsonFileName = parentName;
|
|
235
|
-
} else {
|
|
236
|
-
uiPageDir = join(process.cwd(), "src", "ui", pageName);
|
|
237
|
-
localePagePath = join(srcPath, pageName);
|
|
238
|
-
jsonFileName = pageName;
|
|
239
|
-
}
|
|
240
|
-
if (!existsSync(uiPageDir)) {
|
|
241
|
-
await mkdir(uiPageDir, { recursive: true });
|
|
242
|
-
}
|
|
243
|
-
const uiPageFile = join(uiPageDir, "page-ui.tsx");
|
|
244
|
-
const uiPageTemplate = join(templatePath, "page-ui.tsx");
|
|
245
|
-
if (existsSync(uiPageTemplate)) {
|
|
246
|
-
let uiContent = await readFile(uiPageTemplate, "utf-8");
|
|
247
|
-
uiContent = uiContent
|
|
248
|
-
.replace(/template/g, childName || pageName)
|
|
249
|
-
.replace(/Template/g, capitalize(childName || pageName));
|
|
250
|
-
await writeFile(uiPageFile, uiContent);
|
|
251
|
-
console.log(`📄 File created: ${uiPageFile}`);
|
|
252
|
-
} else {
|
|
253
|
-
console.warn("⚠️ Template page-ui.tsx manquant.");
|
|
254
|
-
}
|
|
255
|
-
if (!existsSync(localePagePath)) {
|
|
256
|
-
await mkdir(localePagePath, { recursive: true });
|
|
257
|
-
}
|
|
258
|
-
for (const flag of flags) {
|
|
259
|
-
const filename = toFileName(flag);
|
|
260
|
-
const src = join(templatePath, filename);
|
|
261
|
-
const dst = join(localePagePath, filename);
|
|
262
|
-
if (!existsSync(src)) {
|
|
263
|
-
console.warn(`⚠️ Missing template file: ${filename}`);
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
const content = await readFile(src, "utf-8");
|
|
267
|
-
const replaced = content
|
|
268
|
-
.replace(/template/g, childName || pageName)
|
|
269
|
-
.replace(/Template/g, capitalize(childName || pageName));
|
|
270
|
-
await writeFile(dst, replaced);
|
|
271
|
-
console.log(`📄 File created: ${dst}`);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Add JSON to parent object if nested, otherwise create a simple file
|
|
275
|
-
const jsonTemplate = join(templatePath, "page.json");
|
|
276
|
-
if (!existsSync(jsonTemplate)) {
|
|
277
|
-
console.warn("⚠️ Missing template page.json.");
|
|
278
|
-
}
|
|
279
|
-
const content = await readFile(jsonTemplate, "utf-8");
|
|
280
|
-
const replaced = content
|
|
281
|
-
.replace(/template/g, childName || pageName)
|
|
282
|
-
.replace(/Template/g, capitalize(childName || pageName));
|
|
283
|
-
for (const locale of locales) {
|
|
284
|
-
// Only process if messages/<locale> is a directory
|
|
285
|
-
const localeDir = join(messagesPath, locale);
|
|
286
|
-
if (
|
|
287
|
-
!existsSync(localeDir) ||
|
|
288
|
-
!require("node:fs").statSync(localeDir).isDirectory()
|
|
289
|
-
)
|
|
290
|
-
continue;
|
|
291
|
-
const jsonTarget = join(messagesPath, locale, `${jsonFileName}.json`);
|
|
292
|
-
let current: Record<string, any> = {};
|
|
293
|
-
if (existsSync(jsonTarget)) {
|
|
294
|
-
const jsonFile = await readFile(jsonTarget, "utf-8");
|
|
295
|
-
try {
|
|
296
|
-
current = JSON.parse(jsonFile) as Record<string, any>;
|
|
297
|
-
} catch {
|
|
298
|
-
current = {};
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
if (parentName && childName) {
|
|
302
|
-
current[childName] = JSON.parse(replaced);
|
|
303
|
-
} else {
|
|
304
|
-
// fichier simple
|
|
305
|
-
current = JSON.parse(replaced);
|
|
306
|
-
}
|
|
307
|
-
await writeFile(jsonTarget, JSON.stringify(current, null, 2));
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
console.log(`✅ Page "${pageName}" with templates added for each locale.`);
|
|
41
|
+
addPage(args);
|
|
311
42
|
return;
|
|
312
43
|
}
|
|
313
44
|
|
|
@@ -315,57 +46,7 @@ export async function main() {
|
|
|
315
46
|
* Handle rmpage command: remove a page and all related files/folders.
|
|
316
47
|
*/
|
|
317
48
|
if (args[0] === "rmpage") {
|
|
318
|
-
|
|
319
|
-
if (!pageName || pageName.startsWith("-")) {
|
|
320
|
-
const response = await prompts.prompt({
|
|
321
|
-
type: "text",
|
|
322
|
-
name: "pageName",
|
|
323
|
-
message: "🗑️ Page name to remove:",
|
|
324
|
-
validate: (name: string) => (name ? true : "Page name is required"),
|
|
325
|
-
});
|
|
326
|
-
pageName = response.pageName;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Remove translation files messages/<lang>/<PageName>.json
|
|
330
|
-
const messagesPath = join(process.cwd(), "messages");
|
|
331
|
-
const entries = await readdir(messagesPath, { withFileTypes: true });
|
|
332
|
-
const langDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
333
|
-
for (const locale of langDirs) {
|
|
334
|
-
const jsonTarget = join(messagesPath, locale, `${pageName}.json`);
|
|
335
|
-
if (existsSync(jsonTarget)) {
|
|
336
|
-
await writeFile(jsonTarget, "");
|
|
337
|
-
await import("node:child_process").then((cp) =>
|
|
338
|
-
cp.execSync(`rm -f '${jsonTarget}'`)
|
|
339
|
-
);
|
|
340
|
-
console.log(`🗑️ Deleted: ${jsonTarget}`);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Remove folder src/ui/<PageName>
|
|
345
|
-
const uiPageDir = join(process.cwd(), "src", "ui", pageName);
|
|
346
|
-
if (existsSync(uiPageDir)) {
|
|
347
|
-
await import("node:child_process").then((cp) =>
|
|
348
|
-
cp.execSync(`rm -rf '${uiPageDir}'`)
|
|
349
|
-
);
|
|
350
|
-
console.log(`🗑️ Deleted: ${uiPageDir}`);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Remove folder src/app/[locale]/<PageName>
|
|
354
|
-
const appLocaleDir = join(
|
|
355
|
-
process.cwd(),
|
|
356
|
-
"src",
|
|
357
|
-
"app",
|
|
358
|
-
"[locale]",
|
|
359
|
-
pageName
|
|
360
|
-
);
|
|
361
|
-
if (existsSync(appLocaleDir)) {
|
|
362
|
-
await import("node:child_process").then((cp) =>
|
|
363
|
-
cp.execSync(`rm -rf '${appLocaleDir}'`)
|
|
364
|
-
);
|
|
365
|
-
console.log(`🗑️ Deleted: ${appLocaleDir}`);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
console.log(`✅ Page "${pageName}" deleted.`);
|
|
49
|
+
rmPage(args);
|
|
369
50
|
return;
|
|
370
51
|
}
|
|
371
52
|
|
|
@@ -375,137 +56,12 @@ export async function main() {
|
|
|
375
56
|
const nameArg = args.find((arg) => !arg.startsWith("--"));
|
|
376
57
|
|
|
377
58
|
if (nameArg) {
|
|
378
|
-
|
|
379
|
-
projectName: nameArg,
|
|
380
|
-
useTypescript: true,
|
|
381
|
-
useEslint: true,
|
|
382
|
-
useTailwind: true,
|
|
383
|
-
useSrcDir: true,
|
|
384
|
-
useTurbopack: true,
|
|
385
|
-
useI18n: true,
|
|
386
|
-
customAlias: false,
|
|
387
|
-
importAlias: "@/*",
|
|
388
|
-
force,
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
console.log(`📦 Creating project "${response.projectName}"...`);
|
|
392
|
-
await scaffoldProject(response);
|
|
59
|
+
createProject(nameArg, force);
|
|
393
60
|
return;
|
|
394
61
|
}
|
|
395
62
|
|
|
396
63
|
/**
|
|
397
64
|
* Interactive prompt for project creation (not currently used, see note above).
|
|
398
65
|
*/
|
|
399
|
-
|
|
400
|
-
{
|
|
401
|
-
type: "text",
|
|
402
|
-
name: "projectName",
|
|
403
|
-
message: "🧱 Project name:",
|
|
404
|
-
initial: "my-next-app",
|
|
405
|
-
},
|
|
406
|
-
{
|
|
407
|
-
type: "toggle",
|
|
408
|
-
name: "useTypescript",
|
|
409
|
-
message: "✔ Use TypeScript?",
|
|
410
|
-
initial: true,
|
|
411
|
-
active: "Yes",
|
|
412
|
-
inactive: "No",
|
|
413
|
-
},
|
|
414
|
-
{
|
|
415
|
-
type: "toggle",
|
|
416
|
-
name: "useEslint",
|
|
417
|
-
message: "✔ Use ESLint?",
|
|
418
|
-
initial: true,
|
|
419
|
-
active: "Yes",
|
|
420
|
-
inactive: "No",
|
|
421
|
-
},
|
|
422
|
-
{
|
|
423
|
-
type: "toggle",
|
|
424
|
-
name: "useTailwind",
|
|
425
|
-
message: "✔ Use Tailwind CSS?",
|
|
426
|
-
initial: true,
|
|
427
|
-
active: "Yes",
|
|
428
|
-
inactive: "No",
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
type: "toggle",
|
|
432
|
-
name: "useSrcDir",
|
|
433
|
-
message: "✔ Use `src/` directory?",
|
|
434
|
-
initial: false,
|
|
435
|
-
active: "Yes",
|
|
436
|
-
inactive: "No",
|
|
437
|
-
},
|
|
438
|
-
{
|
|
439
|
-
type: "toggle",
|
|
440
|
-
name: "useTurbopack",
|
|
441
|
-
message: "✔ Use Turbopack for `next dev`?",
|
|
442
|
-
initial: true,
|
|
443
|
-
active: "Yes",
|
|
444
|
-
inactive: "No",
|
|
445
|
-
},
|
|
446
|
-
{
|
|
447
|
-
type: "toggle",
|
|
448
|
-
name: "useI18n",
|
|
449
|
-
message: "✔ Use i18n with next-intl for translations?",
|
|
450
|
-
initial: true,
|
|
451
|
-
active: "Yes",
|
|
452
|
-
inactive: "No",
|
|
453
|
-
},
|
|
454
|
-
{
|
|
455
|
-
type: "toggle",
|
|
456
|
-
name: "customAlias",
|
|
457
|
-
message: "✔ Customize import alias (`@/*` by default)?",
|
|
458
|
-
initial: false,
|
|
459
|
-
active: "Yes",
|
|
460
|
-
inactive: "No",
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
type: (prev: boolean) => (prev ? "text" : null),
|
|
464
|
-
name: "importAlias",
|
|
465
|
-
message: "✔ What import alias would you like?",
|
|
466
|
-
initial: "@core/*",
|
|
467
|
-
},
|
|
468
|
-
]);
|
|
469
|
-
|
|
470
|
-
console.log("\n✅ Your choices:");
|
|
471
|
-
console.log(response);
|
|
472
|
-
|
|
473
|
-
await scaffoldProject(response);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Capitalize the first letter of a string.
|
|
478
|
-
*/
|
|
479
|
-
function capitalize(str: string): string {
|
|
480
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Map a key to its corresponding file name for page/component templates.
|
|
485
|
-
* @param key string
|
|
486
|
-
* @returns file name string
|
|
487
|
-
*/
|
|
488
|
-
function toFileName(key: string): string {
|
|
489
|
-
switch (key) {
|
|
490
|
-
case "layout":
|
|
491
|
-
return "layout.tsx";
|
|
492
|
-
case "page":
|
|
493
|
-
return "page.tsx";
|
|
494
|
-
case "loading":
|
|
495
|
-
return "loading.tsx";
|
|
496
|
-
case "not-found":
|
|
497
|
-
return "not-found.tsx";
|
|
498
|
-
case "error":
|
|
499
|
-
return "error.tsx";
|
|
500
|
-
case "global-error":
|
|
501
|
-
return "global-error.tsx";
|
|
502
|
-
case "route":
|
|
503
|
-
return "route.ts";
|
|
504
|
-
case "template":
|
|
505
|
-
return "template.tsx";
|
|
506
|
-
case "default":
|
|
507
|
-
return "default.tsx";
|
|
508
|
-
default:
|
|
509
|
-
return `${key}.tsx`;
|
|
510
|
-
}
|
|
66
|
+
await createProjectWithPrompt();
|
|
511
67
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { mkdir, readFile, writeFile, readdir } from "node:fs/promises";
|
|
3
|
+
import prompts from "prompts";
|
|
4
|
+
import { capitalize } from "./utils"; // Assuming you have a utility function to capitalize strings
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
|
|
7
|
+
export async function addComponent(args: string[]) {
|
|
8
|
+
let componentName = args[1];
|
|
9
|
+
let pageScope = null;
|
|
10
|
+
let pageIndex = args.findIndex((arg) => arg === "-P" || arg === "--page");
|
|
11
|
+
if (pageIndex !== -1 && args[pageIndex + 1]) {
|
|
12
|
+
pageScope = args[pageIndex + 1];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Handle nested pageScope (e.g. ParentPage.ChildPage)
|
|
16
|
+
let nestedPath = null;
|
|
17
|
+
if (pageScope && pageScope.includes(".")) {
|
|
18
|
+
nestedPath = join(...pageScope.split("."));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!componentName || componentName.startsWith("-")) {
|
|
22
|
+
// Si le nom n'est pas fourni ou est une option, demander via prompt
|
|
23
|
+
const response = await prompts.prompt({
|
|
24
|
+
type: "text",
|
|
25
|
+
name: "componentName",
|
|
26
|
+
message: "🧩 Component name to add:",
|
|
27
|
+
validate: (name: string) => (name ? true : "Component name is required"),
|
|
28
|
+
});
|
|
29
|
+
componentName = response.componentName;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const componentNameUpper = capitalize(componentName);
|
|
33
|
+
const templatePath = join(import.meta.dir, "..", "templates", "Component");
|
|
34
|
+
const messagesPath = join(process.cwd(), "messages");
|
|
35
|
+
|
|
36
|
+
// Determine target path for the component
|
|
37
|
+
let componentTargetPath;
|
|
38
|
+
let translationKey;
|
|
39
|
+
if (pageScope) {
|
|
40
|
+
if (nestedPath) {
|
|
41
|
+
componentTargetPath = join(process.cwd(), "src", "ui", nestedPath);
|
|
42
|
+
translationKey = pageScope;
|
|
43
|
+
} else {
|
|
44
|
+
componentTargetPath = join(process.cwd(), "src", "ui", pageScope);
|
|
45
|
+
translationKey = pageScope;
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
componentTargetPath = join(process.cwd(), "src", "ui", "_global");
|
|
49
|
+
translationKey = "_global_ui";
|
|
50
|
+
}
|
|
51
|
+
if (!existsSync(componentTargetPath)) {
|
|
52
|
+
await mkdir(componentTargetPath, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
const componentFile = join(componentTargetPath, `${componentNameUpper}.tsx`);
|
|
55
|
+
|
|
56
|
+
// Read and adapt the TSX template
|
|
57
|
+
const templateComponentPath = join(templatePath, "Component.tsx");
|
|
58
|
+
if (existsSync(templateComponentPath)) {
|
|
59
|
+
let content = await readFile(templateComponentPath, "utf-8");
|
|
60
|
+
// Remplacement du nom du component et de la clé de traduction
|
|
61
|
+
content = content
|
|
62
|
+
.replace(/Component/g, componentNameUpper)
|
|
63
|
+
.replace(/componentPage/g, translationKey);
|
|
64
|
+
await writeFile(componentFile, content);
|
|
65
|
+
console.log(`📄 File created: ${componentFile}`);
|
|
66
|
+
} else {
|
|
67
|
+
console.error(
|
|
68
|
+
"❌ Template Component.tsx introuvable :",
|
|
69
|
+
templateComponentPath
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Add the component to each language's JSON file (only folders)
|
|
74
|
+
const entries = await readdir(messagesPath, { withFileTypes: true });
|
|
75
|
+
const langDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
76
|
+
const jsonTemplate = join(templatePath, "component.json");
|
|
77
|
+
if (!existsSync(jsonTemplate)) {
|
|
78
|
+
console.error("❌ Template component.json not found:", jsonTemplate);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const jsonContent = await readFile(jsonTemplate, "utf-8");
|
|
82
|
+
const parsed = JSON.parse(jsonContent);
|
|
83
|
+
|
|
84
|
+
for (const locale of langDirs) {
|
|
85
|
+
// Only process if messages/<locale> is a directory
|
|
86
|
+
const localeDir = join(messagesPath, locale);
|
|
87
|
+
if (
|
|
88
|
+
!existsSync(localeDir) ||
|
|
89
|
+
!require("node:fs").statSync(localeDir).isDirectory()
|
|
90
|
+
)
|
|
91
|
+
continue;
|
|
92
|
+
let jsonTarget;
|
|
93
|
+
if (pageScope) {
|
|
94
|
+
jsonTarget = join(messagesPath, locale, `${pageScope}.json`);
|
|
95
|
+
} else {
|
|
96
|
+
jsonTarget = join(messagesPath, locale, `_global_ui.json`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let current: Record<string, any> = {};
|
|
100
|
+
if (existsSync(jsonTarget)) {
|
|
101
|
+
const jsonFile = await readFile(jsonTarget, "utf-8");
|
|
102
|
+
current = JSON.parse(jsonFile) as Record<string, any>;
|
|
103
|
+
}
|
|
104
|
+
current[componentNameUpper] = parsed;
|
|
105
|
+
await writeFile(jsonTarget, JSON.stringify(current, null, 2));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(
|
|
109
|
+
`✅ Component "${componentNameUpper}" added ${
|
|
110
|
+
pageScope ? `to page ${pageScope}` : "globally"
|
|
111
|
+
} with localized messages.`
|
|
112
|
+
);
|
|
113
|
+
}
|