create-bcp-app 0.1.12 → 0.1.13
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/bin/create-bcp-app.mjs +291 -433
- package/package.json +1 -1
- package/src/index.mjs +62 -5
package/bin/create-bcp-app.mjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
} from "node:readline
|
|
5
|
+
emitKeypressEvents,
|
|
6
|
+
} from "node:readline";
|
|
7
7
|
import {
|
|
8
8
|
stdin as input,
|
|
9
9
|
stdout as output,
|
|
@@ -19,11 +19,16 @@ import {
|
|
|
19
19
|
normalizeDatabase,
|
|
20
20
|
} from "../src/project-options.mjs";
|
|
21
21
|
|
|
22
|
+
const ANSI = {
|
|
23
|
+
reset: "\u001B[0m",
|
|
24
|
+
selected: "\u001B[46m\u001B[30m\u001B[1m",
|
|
25
|
+
muted: "\u001B[2m",
|
|
26
|
+
};
|
|
27
|
+
|
|
22
28
|
await main();
|
|
23
29
|
|
|
24
30
|
async function main() {
|
|
25
|
-
const args =
|
|
26
|
-
process.argv.slice(2);
|
|
31
|
+
const args = process.argv.slice(2);
|
|
27
32
|
|
|
28
33
|
if (
|
|
29
34
|
args.includes("--help") ||
|
|
@@ -36,35 +41,20 @@ async function main() {
|
|
|
36
41
|
let parsed;
|
|
37
42
|
|
|
38
43
|
try {
|
|
39
|
-
parsed =
|
|
40
|
-
parseArguments(
|
|
41
|
-
args
|
|
42
|
-
);
|
|
44
|
+
parsed = parseArguments(args);
|
|
43
45
|
} catch (error) {
|
|
44
|
-
reportError(
|
|
45
|
-
error
|
|
46
|
-
);
|
|
46
|
+
reportError(error);
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
if (!parsed.projectDirectory) {
|
|
51
51
|
printHelp();
|
|
52
|
-
process.exitCode =
|
|
53
|
-
1;
|
|
52
|
+
process.exitCode = 1;
|
|
54
53
|
return;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
let prompt =
|
|
58
|
-
null;
|
|
59
|
-
|
|
60
56
|
try {
|
|
61
|
-
const resolved =
|
|
62
|
-
await resolveOptions(
|
|
63
|
-
parsed
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
prompt =
|
|
67
|
-
resolved.prompt;
|
|
57
|
+
const resolved = await resolveOptions(parsed);
|
|
68
58
|
|
|
69
59
|
console.log("");
|
|
70
60
|
console.log(
|
|
@@ -74,68 +64,40 @@ async function main() {
|
|
|
74
64
|
);
|
|
75
65
|
console.log("");
|
|
76
66
|
|
|
77
|
-
const result =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
resolved.database,
|
|
89
|
-
auth:
|
|
90
|
-
resolved.auth,
|
|
91
|
-
packageManager:
|
|
92
|
-
"npm",
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
printSuccess(
|
|
96
|
-
result,
|
|
97
|
-
parsed.install
|
|
98
|
-
);
|
|
67
|
+
const result = await createBcpApp({
|
|
68
|
+
projectDirectory: parsed.projectDirectory,
|
|
69
|
+
install: parsed.install,
|
|
70
|
+
bcpPackage: parsed.bcpPackage,
|
|
71
|
+
tailwind: resolved.tailwind,
|
|
72
|
+
database: resolved.database,
|
|
73
|
+
auth: resolved.auth,
|
|
74
|
+
packageManager: "npm",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
printSuccess(result, parsed.install);
|
|
99
78
|
} catch (error) {
|
|
100
|
-
reportError(
|
|
101
|
-
error
|
|
102
|
-
);
|
|
103
|
-
} finally {
|
|
104
|
-
prompt?.close();
|
|
79
|
+
reportError(error);
|
|
105
80
|
}
|
|
106
81
|
}
|
|
107
82
|
|
|
108
|
-
function parseArguments(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
let
|
|
112
|
-
|
|
113
|
-
let
|
|
114
|
-
|
|
115
|
-
let
|
|
116
|
-
undefined;
|
|
117
|
-
let tailwind =
|
|
118
|
-
undefined;
|
|
119
|
-
let database =
|
|
120
|
-
undefined;
|
|
121
|
-
let auth =
|
|
122
|
-
undefined;
|
|
123
|
-
let acceptDefaults =
|
|
124
|
-
false;
|
|
83
|
+
function parseArguments(args) {
|
|
84
|
+
let projectDirectory = null;
|
|
85
|
+
let install = true;
|
|
86
|
+
let bcpPackage = undefined;
|
|
87
|
+
let tailwind = undefined;
|
|
88
|
+
let database = undefined;
|
|
89
|
+
let auth = undefined;
|
|
90
|
+
let acceptDefaults = false;
|
|
125
91
|
|
|
126
92
|
for (
|
|
127
93
|
let index = 0;
|
|
128
94
|
index < args.length;
|
|
129
95
|
index++
|
|
130
96
|
) {
|
|
131
|
-
const value =
|
|
132
|
-
args[index];
|
|
97
|
+
const value = args[index];
|
|
133
98
|
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
) {
|
|
137
|
-
install =
|
|
138
|
-
false;
|
|
99
|
+
if (value === "--no-install") {
|
|
100
|
+
install = false;
|
|
139
101
|
continue;
|
|
140
102
|
}
|
|
141
103
|
|
|
@@ -143,34 +105,22 @@ function parseArguments(
|
|
|
143
105
|
value === "--yes" ||
|
|
144
106
|
value === "-y"
|
|
145
107
|
) {
|
|
146
|
-
acceptDefaults =
|
|
147
|
-
true;
|
|
108
|
+
acceptDefaults = true;
|
|
148
109
|
continue;
|
|
149
110
|
}
|
|
150
111
|
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
) {
|
|
154
|
-
tailwind =
|
|
155
|
-
true;
|
|
112
|
+
if (value === "--tailwind") {
|
|
113
|
+
tailwind = true;
|
|
156
114
|
continue;
|
|
157
115
|
}
|
|
158
116
|
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
) {
|
|
162
|
-
tailwind =
|
|
163
|
-
false;
|
|
117
|
+
if (value === "--no-tailwind") {
|
|
118
|
+
tailwind = false;
|
|
164
119
|
continue;
|
|
165
120
|
}
|
|
166
121
|
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
) {
|
|
170
|
-
const nextValue =
|
|
171
|
-
args[
|
|
172
|
-
index + 1
|
|
173
|
-
];
|
|
122
|
+
if (value === "--database") {
|
|
123
|
+
const nextValue = args[index + 1];
|
|
174
124
|
|
|
175
125
|
if (!nextValue) {
|
|
176
126
|
throw new Error(
|
|
@@ -178,21 +128,13 @@ function parseArguments(
|
|
|
178
128
|
);
|
|
179
129
|
}
|
|
180
130
|
|
|
181
|
-
database =
|
|
182
|
-
normalizeDatabase(
|
|
183
|
-
nextValue
|
|
184
|
-
);
|
|
131
|
+
database = normalizeDatabase(nextValue);
|
|
185
132
|
index++;
|
|
186
133
|
continue;
|
|
187
134
|
}
|
|
188
135
|
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
) {
|
|
192
|
-
const nextValue =
|
|
193
|
-
args[
|
|
194
|
-
index + 1
|
|
195
|
-
];
|
|
136
|
+
if (value === "--auth") {
|
|
137
|
+
const nextValue = args[index + 1];
|
|
196
138
|
|
|
197
139
|
if (!nextValue) {
|
|
198
140
|
throw new Error(
|
|
@@ -200,21 +142,13 @@ function parseArguments(
|
|
|
200
142
|
);
|
|
201
143
|
}
|
|
202
144
|
|
|
203
|
-
auth =
|
|
204
|
-
normalizeAuth(
|
|
205
|
-
nextValue
|
|
206
|
-
);
|
|
145
|
+
auth = normalizeAuth(nextValue);
|
|
207
146
|
index++;
|
|
208
147
|
continue;
|
|
209
148
|
}
|
|
210
149
|
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
) {
|
|
214
|
-
const nextValue =
|
|
215
|
-
args[
|
|
216
|
-
index + 1
|
|
217
|
-
];
|
|
150
|
+
if (value === "--bcp") {
|
|
151
|
+
const nextValue = args[index + 1];
|
|
218
152
|
|
|
219
153
|
if (!nextValue) {
|
|
220
154
|
throw new Error(
|
|
@@ -222,31 +156,24 @@ function parseArguments(
|
|
|
222
156
|
);
|
|
223
157
|
}
|
|
224
158
|
|
|
225
|
-
bcpPackage =
|
|
226
|
-
nextValue;
|
|
159
|
+
bcpPackage = nextValue;
|
|
227
160
|
index++;
|
|
228
161
|
continue;
|
|
229
162
|
}
|
|
230
163
|
|
|
231
|
-
if (
|
|
232
|
-
value.startsWith("-")
|
|
233
|
-
) {
|
|
164
|
+
if (value.startsWith("-")) {
|
|
234
165
|
throw new Error(
|
|
235
166
|
`Unknown option: ${value}`
|
|
236
167
|
);
|
|
237
168
|
}
|
|
238
169
|
|
|
239
|
-
if (
|
|
240
|
-
projectDirectory !==
|
|
241
|
-
null
|
|
242
|
-
) {
|
|
170
|
+
if (projectDirectory !== null) {
|
|
243
171
|
throw new Error(
|
|
244
172
|
"Only one project directory may be provided."
|
|
245
173
|
);
|
|
246
174
|
}
|
|
247
175
|
|
|
248
|
-
projectDirectory =
|
|
249
|
-
value;
|
|
176
|
+
projectDirectory = value;
|
|
250
177
|
}
|
|
251
178
|
|
|
252
179
|
return {
|
|
@@ -266,196 +193,79 @@ async function resolveOptions({
|
|
|
266
193
|
auth: selectedAuth,
|
|
267
194
|
acceptDefaults,
|
|
268
195
|
}) {
|
|
269
|
-
let tailwind =
|
|
270
|
-
|
|
271
|
-
let
|
|
272
|
-
selectedDatabase;
|
|
273
|
-
let auth =
|
|
274
|
-
selectedAuth;
|
|
196
|
+
let tailwind = selectedTailwind;
|
|
197
|
+
let database = selectedDatabase;
|
|
198
|
+
let auth = selectedAuth;
|
|
275
199
|
|
|
276
200
|
if (acceptDefaults) {
|
|
277
201
|
return {
|
|
278
|
-
tailwind:
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
),
|
|
286
|
-
auth:
|
|
287
|
-
normalizeAuth(
|
|
288
|
-
auth ??
|
|
289
|
-
"none"
|
|
290
|
-
),
|
|
291
|
-
prompt:
|
|
292
|
-
null,
|
|
202
|
+
tailwind: tailwind ?? true,
|
|
203
|
+
database: normalizeDatabase(
|
|
204
|
+
database ?? "none"
|
|
205
|
+
),
|
|
206
|
+
auth: normalizeAuth(
|
|
207
|
+
auth ?? "none"
|
|
208
|
+
),
|
|
293
209
|
};
|
|
294
210
|
}
|
|
295
211
|
|
|
296
|
-
const interactive =
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
);
|
|
212
|
+
const interactive = Boolean(
|
|
213
|
+
input.isTTY &&
|
|
214
|
+
output.isTTY
|
|
215
|
+
);
|
|
301
216
|
|
|
302
217
|
if (!interactive) {
|
|
303
218
|
return {
|
|
304
|
-
tailwind:
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
),
|
|
312
|
-
auth:
|
|
313
|
-
normalizeAuth(
|
|
314
|
-
auth ??
|
|
315
|
-
"none"
|
|
316
|
-
),
|
|
317
|
-
prompt:
|
|
318
|
-
null,
|
|
219
|
+
tailwind: tailwind ?? false,
|
|
220
|
+
database: normalizeDatabase(
|
|
221
|
+
database ?? "none"
|
|
222
|
+
),
|
|
223
|
+
auth: normalizeAuth(
|
|
224
|
+
auth ?? "none"
|
|
225
|
+
),
|
|
319
226
|
};
|
|
320
227
|
}
|
|
321
228
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
auth === undefined;
|
|
229
|
+
console.log("");
|
|
230
|
+
console.log("Configure your BCP app");
|
|
231
|
+
console.log("");
|
|
326
232
|
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
tailwind:
|
|
330
|
-
Boolean(
|
|
331
|
-
tailwind
|
|
332
|
-
),
|
|
333
|
-
database:
|
|
334
|
-
normalizeDatabase(
|
|
335
|
-
database
|
|
336
|
-
),
|
|
337
|
-
auth:
|
|
338
|
-
normalizeAuth(
|
|
339
|
-
auth
|
|
340
|
-
),
|
|
341
|
-
prompt:
|
|
342
|
-
null,
|
|
343
|
-
};
|
|
233
|
+
if (tailwind === undefined) {
|
|
234
|
+
tailwind = await askTailwind();
|
|
344
235
|
}
|
|
345
236
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
output,
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
try {
|
|
353
|
-
console.log("");
|
|
354
|
-
console.log(
|
|
355
|
-
"Configure your BCP app"
|
|
356
|
-
);
|
|
357
|
-
console.log("");
|
|
358
|
-
|
|
359
|
-
if (
|
|
360
|
-
tailwind ===
|
|
361
|
-
undefined
|
|
362
|
-
) {
|
|
363
|
-
tailwind =
|
|
364
|
-
await askYesNo(
|
|
365
|
-
prompt,
|
|
366
|
-
"Use Tailwind CSS?",
|
|
367
|
-
true
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (
|
|
372
|
-
database ===
|
|
373
|
-
undefined
|
|
374
|
-
) {
|
|
375
|
-
database =
|
|
376
|
-
await askDatabase(
|
|
377
|
-
prompt
|
|
378
|
-
);
|
|
379
|
-
}
|
|
237
|
+
if (database === undefined) {
|
|
238
|
+
database = await askDatabase();
|
|
239
|
+
}
|
|
380
240
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
undefined
|
|
384
|
-
) {
|
|
385
|
-
auth =
|
|
386
|
-
await askAuth(
|
|
387
|
-
prompt
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
} catch (error) {
|
|
391
|
-
prompt.close();
|
|
392
|
-
throw error;
|
|
241
|
+
if (auth === undefined) {
|
|
242
|
+
auth = await askAuth();
|
|
393
243
|
}
|
|
394
244
|
|
|
395
245
|
return {
|
|
396
|
-
tailwind:
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
),
|
|
400
|
-
database:
|
|
401
|
-
normalizeDatabase(
|
|
402
|
-
database
|
|
403
|
-
),
|
|
404
|
-
auth:
|
|
405
|
-
normalizeAuth(
|
|
406
|
-
auth
|
|
407
|
-
),
|
|
408
|
-
prompt,
|
|
246
|
+
tailwind: Boolean(tailwind),
|
|
247
|
+
database: normalizeDatabase(database),
|
|
248
|
+
auth: normalizeAuth(auth),
|
|
409
249
|
};
|
|
410
250
|
}
|
|
411
251
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
`${label}${suffix}`
|
|
427
|
-
)
|
|
428
|
-
)
|
|
429
|
-
.trim()
|
|
430
|
-
.toLowerCase();
|
|
431
|
-
|
|
432
|
-
if (!answer) {
|
|
433
|
-
return defaultValue;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
if (
|
|
437
|
-
answer === "y" ||
|
|
438
|
-
answer === "yes"
|
|
439
|
-
) {
|
|
440
|
-
return true;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (
|
|
444
|
-
answer === "n" ||
|
|
445
|
-
answer === "no"
|
|
446
|
-
) {
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
console.log(
|
|
451
|
-
"Please answer yes or no."
|
|
452
|
-
);
|
|
453
|
-
}
|
|
252
|
+
function askTailwind() {
|
|
253
|
+
return askTerminalSelect(
|
|
254
|
+
"Use Tailwind CSS?",
|
|
255
|
+
[
|
|
256
|
+
{
|
|
257
|
+
value: true,
|
|
258
|
+
label: "Yes",
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
value: false,
|
|
262
|
+
label: "No",
|
|
263
|
+
},
|
|
264
|
+
]
|
|
265
|
+
);
|
|
454
266
|
}
|
|
455
267
|
|
|
456
|
-
|
|
457
|
-
prompt
|
|
458
|
-
) {
|
|
268
|
+
function askDatabase() {
|
|
459
269
|
const labels = {
|
|
460
270
|
none: "None",
|
|
461
271
|
mysql: "MySQL",
|
|
@@ -464,130 +274,201 @@ async function askDatabase(
|
|
|
464
274
|
mongodb: "MongoDB",
|
|
465
275
|
};
|
|
466
276
|
|
|
467
|
-
|
|
468
|
-
"Select a database:"
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
) => {
|
|
476
|
-
console.log(
|
|
477
|
-
` ${index + 1}) ${labels[value]}`
|
|
478
|
-
);
|
|
479
|
-
}
|
|
277
|
+
return askTerminalSelect(
|
|
278
|
+
"Select a database:",
|
|
279
|
+
DATABASE_CHOICES.map(
|
|
280
|
+
(value) => ({
|
|
281
|
+
value,
|
|
282
|
+
label: labels[value],
|
|
283
|
+
})
|
|
284
|
+
)
|
|
480
285
|
);
|
|
481
|
-
|
|
482
|
-
while (true) {
|
|
483
|
-
const answer =
|
|
484
|
-
(
|
|
485
|
-
await prompt.question(
|
|
486
|
-
"Database (1): "
|
|
487
|
-
)
|
|
488
|
-
).trim();
|
|
489
|
-
|
|
490
|
-
if (!answer) {
|
|
491
|
-
return "none";
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const choice =
|
|
495
|
-
DATABASE_CHOICES[
|
|
496
|
-
Number(answer) - 1
|
|
497
|
-
];
|
|
498
|
-
|
|
499
|
-
if (choice) {
|
|
500
|
-
return choice;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
const normalized =
|
|
504
|
-
answer.toLowerCase();
|
|
505
|
-
|
|
506
|
-
if (
|
|
507
|
-
DATABASE_CHOICES.includes(
|
|
508
|
-
normalized
|
|
509
|
-
)
|
|
510
|
-
) {
|
|
511
|
-
return normalized;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
console.log(
|
|
515
|
-
"Choose a number from the list or enter a database name."
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
286
|
}
|
|
519
287
|
|
|
520
|
-
|
|
521
|
-
prompt
|
|
522
|
-
) {
|
|
288
|
+
function askAuth() {
|
|
523
289
|
const labels = {
|
|
524
290
|
none: "None",
|
|
525
|
-
"jwt-cookie":
|
|
526
|
-
"JWT Cookie",
|
|
291
|
+
"jwt-cookie": "JWT Cookie",
|
|
527
292
|
};
|
|
528
293
|
|
|
529
|
-
|
|
530
|
-
"Select authentication:"
|
|
294
|
+
return askTerminalSelect(
|
|
295
|
+
"Select authentication:",
|
|
296
|
+
AUTH_CHOICES.map(
|
|
297
|
+
(value) => ({
|
|
298
|
+
value,
|
|
299
|
+
label: labels[value],
|
|
300
|
+
})
|
|
301
|
+
)
|
|
531
302
|
);
|
|
303
|
+
}
|
|
532
304
|
|
|
533
|
-
|
|
305
|
+
function askTerminalSelect(
|
|
306
|
+
label,
|
|
307
|
+
choices
|
|
308
|
+
) {
|
|
309
|
+
if (
|
|
310
|
+
!Array.isArray(choices) ||
|
|
311
|
+
choices.length === 0
|
|
312
|
+
) {
|
|
313
|
+
return Promise.reject(
|
|
314
|
+
new Error(
|
|
315
|
+
"Interactive selection requires at least one choice."
|
|
316
|
+
)
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (
|
|
321
|
+
typeof input.setRawMode !== "function"
|
|
322
|
+
) {
|
|
323
|
+
return Promise.resolve(
|
|
324
|
+
choices[0].value
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return new Promise(
|
|
534
329
|
(
|
|
535
|
-
|
|
536
|
-
|
|
330
|
+
resolve,
|
|
331
|
+
reject
|
|
537
332
|
) => {
|
|
333
|
+
let selectedIndex = 0;
|
|
334
|
+
const previousRawMode =
|
|
335
|
+
input.isRaw === true;
|
|
336
|
+
|
|
337
|
+
const render = (
|
|
338
|
+
redraw = false
|
|
339
|
+
) => {
|
|
340
|
+
if (redraw) {
|
|
341
|
+
output.write(
|
|
342
|
+
`\u001B[${choices.length}A`
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
for (
|
|
347
|
+
let index = 0;
|
|
348
|
+
index < choices.length;
|
|
349
|
+
index++
|
|
350
|
+
) {
|
|
351
|
+
const choice = choices[index];
|
|
352
|
+
const selected =
|
|
353
|
+
index === selectedIndex;
|
|
354
|
+
|
|
355
|
+
output.write(
|
|
356
|
+
"\u001B[2K\r"
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
if (selected) {
|
|
360
|
+
output.write(
|
|
361
|
+
`${ANSI.selected} > ${choice.label} ${ANSI.reset}\n`
|
|
362
|
+
);
|
|
363
|
+
} else {
|
|
364
|
+
output.write(
|
|
365
|
+
` ${choice.label}\n`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const cleanup = () => {
|
|
372
|
+
input.removeListener(
|
|
373
|
+
"keypress",
|
|
374
|
+
onKeypress
|
|
375
|
+
);
|
|
376
|
+
input.setRawMode(
|
|
377
|
+
previousRawMode
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
if (!previousRawMode) {
|
|
381
|
+
input.pause();
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
const onKeypress = (
|
|
386
|
+
_character,
|
|
387
|
+
key = {}
|
|
388
|
+
) => {
|
|
389
|
+
if (
|
|
390
|
+
key.ctrl &&
|
|
391
|
+
key.name === "c"
|
|
392
|
+
) {
|
|
393
|
+
cleanup();
|
|
394
|
+
output.write("\n");
|
|
395
|
+
reject(
|
|
396
|
+
new Error(
|
|
397
|
+
"Setup cancelled."
|
|
398
|
+
)
|
|
399
|
+
);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (
|
|
404
|
+
key.name === "escape"
|
|
405
|
+
) {
|
|
406
|
+
cleanup();
|
|
407
|
+
output.write("\n");
|
|
408
|
+
reject(
|
|
409
|
+
new Error(
|
|
410
|
+
"Setup cancelled."
|
|
411
|
+
)
|
|
412
|
+
);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (key.name === "up") {
|
|
417
|
+
selectedIndex = (
|
|
418
|
+
selectedIndex - 1 +
|
|
419
|
+
choices.length
|
|
420
|
+
) % choices.length;
|
|
421
|
+
render(true);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (key.name === "down") {
|
|
426
|
+
selectedIndex = (
|
|
427
|
+
selectedIndex + 1
|
|
428
|
+
) % choices.length;
|
|
429
|
+
render(true);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (
|
|
434
|
+
key.name === "return" ||
|
|
435
|
+
key.name === "enter"
|
|
436
|
+
) {
|
|
437
|
+
const selected =
|
|
438
|
+
choices[selectedIndex];
|
|
439
|
+
|
|
440
|
+
cleanup();
|
|
441
|
+
output.write("\n");
|
|
442
|
+
resolve(selected.value);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
console.log(label);
|
|
538
447
|
console.log(
|
|
539
|
-
|
|
448
|
+
`${ANSI.muted}Use ↑/↓ to move, Enter to select.${ANSI.reset}`
|
|
540
449
|
);
|
|
541
|
-
}
|
|
542
|
-
);
|
|
543
|
-
|
|
544
|
-
while (true) {
|
|
545
|
-
const answer =
|
|
546
|
-
(
|
|
547
|
-
await prompt.question(
|
|
548
|
-
"Authentication (1): "
|
|
549
|
-
)
|
|
550
|
-
).trim();
|
|
551
|
-
|
|
552
|
-
if (!answer) {
|
|
553
|
-
return "none";
|
|
554
|
-
}
|
|
555
450
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
451
|
+
emitKeypressEvents(input);
|
|
452
|
+
input.on(
|
|
453
|
+
"keypress",
|
|
454
|
+
onKeypress
|
|
455
|
+
);
|
|
456
|
+
input.setRawMode(true);
|
|
457
|
+
input.resume();
|
|
560
458
|
|
|
561
|
-
|
|
562
|
-
return choice;
|
|
459
|
+
render();
|
|
563
460
|
}
|
|
564
|
-
|
|
565
|
-
const normalized =
|
|
566
|
-
answer.toLowerCase();
|
|
567
|
-
|
|
568
|
-
if (
|
|
569
|
-
AUTH_CHOICES.includes(
|
|
570
|
-
normalized
|
|
571
|
-
)
|
|
572
|
-
) {
|
|
573
|
-
return normalized;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
console.log(
|
|
577
|
-
"Choose a number from the list or enter an authentication preset name."
|
|
578
|
-
);
|
|
579
|
-
}
|
|
461
|
+
);
|
|
580
462
|
}
|
|
581
463
|
|
|
582
464
|
function printSuccess(
|
|
583
465
|
result,
|
|
584
466
|
installed
|
|
585
467
|
) {
|
|
586
|
-
const relative =
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
) || ".";
|
|
468
|
+
const relative = path.relative(
|
|
469
|
+
process.cwd(),
|
|
470
|
+
result.targetDirectory
|
|
471
|
+
) || ".";
|
|
591
472
|
|
|
592
473
|
console.log("");
|
|
593
474
|
console.log(
|
|
@@ -606,28 +487,20 @@ function printSuccess(
|
|
|
606
487
|
`Authentication: ${formatAuthName(result.auth)}`
|
|
607
488
|
);
|
|
608
489
|
console.log("");
|
|
609
|
-
console.log(
|
|
610
|
-
"Next steps:"
|
|
611
|
-
);
|
|
490
|
+
console.log("Next steps:");
|
|
612
491
|
console.log(
|
|
613
492
|
` cd ${quoteIfNeeded(relative)}`
|
|
614
493
|
);
|
|
615
494
|
|
|
616
495
|
if (!installed) {
|
|
617
|
-
console.log(
|
|
618
|
-
" npm install"
|
|
619
|
-
);
|
|
496
|
+
console.log(" npm install");
|
|
620
497
|
}
|
|
621
498
|
|
|
622
|
-
console.log(
|
|
623
|
-
" npm run dev"
|
|
624
|
-
);
|
|
499
|
+
console.log(" npm run dev");
|
|
625
500
|
console.log("");
|
|
626
501
|
}
|
|
627
502
|
|
|
628
|
-
function formatDatabaseName(
|
|
629
|
-
value
|
|
630
|
-
) {
|
|
503
|
+
function formatDatabaseName(value) {
|
|
631
504
|
const names = {
|
|
632
505
|
none: "None",
|
|
633
506
|
mysql: "MySQL",
|
|
@@ -636,21 +509,16 @@ function formatDatabaseName(
|
|
|
636
509
|
mongodb: "MongoDB",
|
|
637
510
|
};
|
|
638
511
|
|
|
639
|
-
return names[value] ??
|
|
640
|
-
value;
|
|
512
|
+
return names[value] ?? value;
|
|
641
513
|
}
|
|
642
514
|
|
|
643
|
-
function formatAuthName(
|
|
644
|
-
value
|
|
645
|
-
) {
|
|
515
|
+
function formatAuthName(value) {
|
|
646
516
|
const names = {
|
|
647
517
|
none: "None",
|
|
648
|
-
"jwt-cookie":
|
|
649
|
-
"JWT Cookie",
|
|
518
|
+
"jwt-cookie": "JWT Cookie",
|
|
650
519
|
};
|
|
651
520
|
|
|
652
|
-
return names[value] ??
|
|
653
|
-
value;
|
|
521
|
+
return names[value] ?? value;
|
|
654
522
|
}
|
|
655
523
|
|
|
656
524
|
function printHelp() {
|
|
@@ -661,7 +529,8 @@ Usage:
|
|
|
661
529
|
npx create-bcp-app <project-name> [options]
|
|
662
530
|
|
|
663
531
|
Interactive setup:
|
|
664
|
-
-
|
|
532
|
+
- Use ↑/↓ and Enter for every setup choice
|
|
533
|
+
- Choose Tailwind CSS: Yes or No
|
|
665
534
|
- Choose a database: None, MySQL, PostgreSQL, SQLite or MongoDB
|
|
666
535
|
- Choose authentication: None or JWT Cookie
|
|
667
536
|
|
|
@@ -684,31 +553,20 @@ Examples:
|
|
|
684
553
|
`);
|
|
685
554
|
}
|
|
686
555
|
|
|
687
|
-
function quoteIfNeeded(
|
|
688
|
-
value
|
|
689
|
-
)
|
|
690
|
-
return /\s/.test(
|
|
691
|
-
value
|
|
692
|
-
)
|
|
693
|
-
? JSON.stringify(
|
|
694
|
-
value
|
|
695
|
-
)
|
|
556
|
+
function quoteIfNeeded(value) {
|
|
557
|
+
return /\s/.test(value)
|
|
558
|
+
? JSON.stringify(value)
|
|
696
559
|
: value;
|
|
697
560
|
}
|
|
698
561
|
|
|
699
|
-
function reportError(
|
|
700
|
-
error
|
|
701
|
-
) {
|
|
562
|
+
function reportError(error) {
|
|
702
563
|
const message =
|
|
703
564
|
error instanceof Error
|
|
704
565
|
? error.message
|
|
705
|
-
: String(
|
|
706
|
-
error
|
|
707
|
-
);
|
|
566
|
+
: String(error);
|
|
708
567
|
|
|
709
568
|
console.error(
|
|
710
569
|
`create-bcp-app: ${message}`
|
|
711
570
|
);
|
|
712
|
-
process.exitCode =
|
|
713
|
-
1;
|
|
571
|
+
process.exitCode = 1;
|
|
714
572
|
}
|
package/package.json
CHANGED
package/src/index.mjs
CHANGED
|
@@ -250,12 +250,9 @@ function copyTemplate(
|
|
|
250
250
|
sourceDirectory,
|
|
251
251
|
targetDirectory
|
|
252
252
|
) {
|
|
253
|
-
|
|
253
|
+
copyDirectoryContents(
|
|
254
254
|
sourceDirectory,
|
|
255
|
-
targetDirectory
|
|
256
|
-
{
|
|
257
|
-
recursive: true,
|
|
258
|
-
}
|
|
255
|
+
targetDirectory
|
|
259
256
|
);
|
|
260
257
|
|
|
261
258
|
const gitignoreTemplate =
|
|
@@ -279,6 +276,66 @@ function copyTemplate(
|
|
|
279
276
|
}
|
|
280
277
|
}
|
|
281
278
|
|
|
279
|
+
function copyDirectoryContents(
|
|
280
|
+
sourceDirectory,
|
|
281
|
+
targetDirectory
|
|
282
|
+
) {
|
|
283
|
+
fs.mkdirSync(
|
|
284
|
+
targetDirectory,
|
|
285
|
+
{
|
|
286
|
+
recursive: true,
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const entries =
|
|
291
|
+
fs.readdirSync(
|
|
292
|
+
sourceDirectory,
|
|
293
|
+
{
|
|
294
|
+
withFileTypes: true,
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
for (
|
|
299
|
+
const entry
|
|
300
|
+
of entries
|
|
301
|
+
) {
|
|
302
|
+
const sourcePath =
|
|
303
|
+
path.join(
|
|
304
|
+
sourceDirectory,
|
|
305
|
+
entry.name
|
|
306
|
+
);
|
|
307
|
+
const targetPath =
|
|
308
|
+
path.join(
|
|
309
|
+
targetDirectory,
|
|
310
|
+
entry.name
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
if (
|
|
314
|
+
entry.isDirectory()
|
|
315
|
+
) {
|
|
316
|
+
copyDirectoryContents(
|
|
317
|
+
sourcePath,
|
|
318
|
+
targetPath
|
|
319
|
+
);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (
|
|
324
|
+
entry.isFile()
|
|
325
|
+
) {
|
|
326
|
+
fs.copyFileSync(
|
|
327
|
+
sourcePath,
|
|
328
|
+
targetPath
|
|
329
|
+
);
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
throw new Error(
|
|
334
|
+
`Unsupported template entry: ${sourcePath}`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
282
339
|
function readPackageMetadata() {
|
|
283
340
|
const packageJson =
|
|
284
341
|
JSON.parse(
|