@dawitworku/projectcli 0.2.1 → 3.0.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/CHANGELOG.md +29 -0
- package/README.md +106 -0
- package/ROADMAP.md +23 -0
- package/docs/demo.cast +445 -0
- package/docs/demo.svg +254637 -0
- package/package.json +13 -3
- package/scripts/release-notes.js +34 -0
- package/src/add.js +164 -1
- package/src/config.js +66 -0
- package/src/core/doctor/index.js +504 -0
- package/src/devcontainer.js +82 -0
- package/src/index.js +408 -37
- package/src/license.js +80 -0
- package/src/plugin.js +140 -0
- package/src/plugins/contributions.js +101 -0
- package/src/plugins/manager.js +38 -0
- package/src/preflight.js +26 -0
- package/src/preset.js +59 -0
- package/src/presets/index.js +124 -0
- package/src/registry/index.js +91 -0
- package/src/registry.js +3 -909
- package/src/registry_legacy.js +911 -0
- package/src/settings.js +32 -3
- package/src/upgrade.js +228 -0
package/src/index.js
CHANGED
|
@@ -28,13 +28,20 @@ try {
|
|
|
28
28
|
const { getLanguages, getFrameworks, getGenerator } = require("./registry");
|
|
29
29
|
const { runSteps } = require("./run");
|
|
30
30
|
const { runAdd } = require("./add");
|
|
31
|
-
const { checkBinaries } = require("./preflight");
|
|
31
|
+
const { checkBinaries, getInstallHint } = require("./preflight");
|
|
32
32
|
const { gitClone, removeGitFolder } = require("./remote");
|
|
33
33
|
const { generateCI, generateDocker } = require("./cicd");
|
|
34
|
+
const { generateDevContainer } = require("./devcontainer");
|
|
35
|
+
const { generateLicense, licenseTypes } = require("./license");
|
|
34
36
|
const { getDescription } = require("./descriptions");
|
|
35
37
|
const { detectLanguage, detectPackageManager } = require("./detect");
|
|
36
|
-
const { loadConfig } = require("./config");
|
|
38
|
+
const { loadConfig, loadProjectConfig } = require("./config");
|
|
37
39
|
const { runConfig } = require("./settings");
|
|
40
|
+
const { runDoctor } = require("./core/doctor");
|
|
41
|
+
const { runPreset } = require("./preset");
|
|
42
|
+
const { getPreset } = require("./presets");
|
|
43
|
+
const { runUpgrade } = require("./upgrade");
|
|
44
|
+
const { runPlugin } = require("./plugin");
|
|
38
45
|
|
|
39
46
|
const RUST_KEYWORDS = new Set(
|
|
40
47
|
[
|
|
@@ -138,6 +145,8 @@ function parseArgs(argv) {
|
|
|
138
145
|
pm: undefined,
|
|
139
146
|
ci: false,
|
|
140
147
|
docker: false,
|
|
148
|
+
devcontainer: false,
|
|
149
|
+
license: undefined,
|
|
141
150
|
learning: false,
|
|
142
151
|
template: undefined,
|
|
143
152
|
};
|
|
@@ -156,6 +165,9 @@ function parseArgs(argv) {
|
|
|
156
165
|
else if (a === "--dry-run") out.dryRun = true;
|
|
157
166
|
else if (a === "--ci") out.ci = true;
|
|
158
167
|
else if (a === "--docker") out.docker = true;
|
|
168
|
+
else if (a === "--devcontainer") out.devcontainer = true;
|
|
169
|
+
else if (a === "--license") out.license = true;
|
|
170
|
+
else if (a === "--no-license") out.license = false;
|
|
159
171
|
else if (a === "--learning") out.learning = true;
|
|
160
172
|
else if (a.startsWith("--template="))
|
|
161
173
|
out.template = a.slice("--template=".length);
|
|
@@ -190,7 +202,15 @@ function splitCommand(argv) {
|
|
|
190
202
|
if (!argv || argv.length === 0) return { cmd: "init", rest: [] };
|
|
191
203
|
const first = argv[0];
|
|
192
204
|
if (typeof first === "string" && !first.startsWith("-")) {
|
|
193
|
-
if (
|
|
205
|
+
if (
|
|
206
|
+
first === "init" ||
|
|
207
|
+
first === "add" ||
|
|
208
|
+
first === "config" ||
|
|
209
|
+
first === "doctor" ||
|
|
210
|
+
first === "upgrade" ||
|
|
211
|
+
first === "preset" ||
|
|
212
|
+
first === "plugin"
|
|
213
|
+
) {
|
|
194
214
|
return { cmd: first, rest: argv.slice(1) };
|
|
195
215
|
}
|
|
196
216
|
}
|
|
@@ -204,11 +224,22 @@ function printHelp() {
|
|
|
204
224
|
console.log(" projectcli # init a new project");
|
|
205
225
|
console.log(" projectcli init # init a new project");
|
|
206
226
|
console.log(" projectcli add # add libraries to current project");
|
|
227
|
+
console.log(" projectcli add ci # add GitHub Actions CI");
|
|
228
|
+
console.log(" projectcli add docker # add Dockerfile");
|
|
229
|
+
console.log(" projectcli add devcontainer # add VS Code devcontainer");
|
|
230
|
+
console.log(" projectcli add license # add LICENSE");
|
|
231
|
+
console.log(" projectcli add lint # add linter defaults");
|
|
232
|
+
console.log(" projectcli add test # add test runner defaults");
|
|
233
|
+
console.log(" projectcli doctor # check a repo and optionally fix");
|
|
234
|
+
console.log(" projectcli preset # manage presets");
|
|
235
|
+
console.log(" projectcli upgrade # upgrade configs safely");
|
|
236
|
+
console.log(" projectcli plugin # manage plugins");
|
|
207
237
|
console.log(" projectcli --list # list all frameworks");
|
|
208
238
|
console.log(
|
|
209
239
|
" projectcli --language <lang> --framework <fw> --name <project>"
|
|
210
240
|
);
|
|
211
241
|
console.log(" projectcli config # configure defaults");
|
|
242
|
+
console.log(" # config files: .projectclirc or projectcli.config.json");
|
|
212
243
|
console.log("");
|
|
213
244
|
console.log("Flags:");
|
|
214
245
|
console.log(" --help, -h Show help");
|
|
@@ -224,8 +255,28 @@ function printHelp() {
|
|
|
224
255
|
);
|
|
225
256
|
console.log(" --ci Auto-add GitHub Actions CI");
|
|
226
257
|
console.log(" --docker Auto-add Dockerfile");
|
|
258
|
+
console.log(" --devcontainer Auto-add VS Code Dev Container");
|
|
259
|
+
console.log(" --license Force-add LICENSE (uses config defaults)");
|
|
260
|
+
console.log(" --no-license Never add LICENSE");
|
|
227
261
|
console.log(" --learning Enable learning mode (shows descriptions)");
|
|
228
262
|
console.log(" --template Clone from a Git repository URL");
|
|
263
|
+
console.log("");
|
|
264
|
+
console.log("Doctor flags:");
|
|
265
|
+
console.log(" --fix Apply safe fixes");
|
|
266
|
+
console.log(" --json JSON output (CI-friendly)");
|
|
267
|
+
console.log(" --ci-only Only check CI");
|
|
268
|
+
|
|
269
|
+
console.log("");
|
|
270
|
+
console.log("Upgrade flags:");
|
|
271
|
+
console.log(" --preview Show what would change");
|
|
272
|
+
console.log(" --only ci Upgrade only CI templates");
|
|
273
|
+
console.log(" --only docker Upgrade Docker templates");
|
|
274
|
+
console.log(" --only devcontainer Upgrade devcontainer templates");
|
|
275
|
+
|
|
276
|
+
console.log("");
|
|
277
|
+
console.log("Plugin commands:");
|
|
278
|
+
console.log(" projectcli plugin list");
|
|
279
|
+
console.log(" projectcli plugin install <id>");
|
|
229
280
|
}
|
|
230
281
|
|
|
231
282
|
const BACK = "__back__";
|
|
@@ -261,17 +312,47 @@ function printStepsPreview(steps) {
|
|
|
261
312
|
console.log("");
|
|
262
313
|
}
|
|
263
314
|
|
|
264
|
-
function
|
|
265
|
-
|
|
315
|
+
function filterExistingWriteFiles(steps, projectRoot) {
|
|
316
|
+
const kept = [];
|
|
317
|
+
const skipped = [];
|
|
318
|
+
for (const step of steps || []) {
|
|
319
|
+
if (step && step.type === "writeFile" && typeof step.path === "string") {
|
|
320
|
+
const target = path.resolve(projectRoot, step.path);
|
|
321
|
+
if (fs.existsSync(target)) {
|
|
322
|
+
skipped.push(step.path);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
kept.push(step);
|
|
327
|
+
}
|
|
328
|
+
return { kept, skipped };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function printList(effectiveConfig) {
|
|
332
|
+
for (const lang of getLanguages(effectiveConfig)) {
|
|
266
333
|
console.log(`${lang}:`);
|
|
267
|
-
for (const fw of getFrameworks(lang)) {
|
|
268
|
-
const gen = getGenerator(lang, fw);
|
|
334
|
+
for (const fw of getFrameworks(lang, effectiveConfig)) {
|
|
335
|
+
const gen = getGenerator(lang, fw, effectiveConfig);
|
|
269
336
|
const note = gen?.notes ? ` - ${gen.notes}` : "";
|
|
270
|
-
|
|
337
|
+
const stability = gen?.stability ? ` [${gen.stability}]` : "";
|
|
338
|
+
console.log(` - ${fw}${stability}${note}`);
|
|
271
339
|
}
|
|
272
340
|
}
|
|
273
341
|
}
|
|
274
342
|
|
|
343
|
+
function formatStabilityBadge(stability) {
|
|
344
|
+
if (stability === "core") return chalk.green("✅ Core");
|
|
345
|
+
if (stability === "experimental") return chalk.yellow("⚠ Experimental");
|
|
346
|
+
if (stability === "community") return chalk.cyan("👥 Community");
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function printMissingTool(bin) {
|
|
351
|
+
console.log(chalk.red(`✖ ${bin} not found`));
|
|
352
|
+
const hint = getInstallHint(bin);
|
|
353
|
+
if (hint) console.log(chalk.dim(` → ${hint}`));
|
|
354
|
+
}
|
|
355
|
+
|
|
275
356
|
function showBanner() {
|
|
276
357
|
console.log("");
|
|
277
358
|
console.log(
|
|
@@ -314,13 +395,20 @@ async function main(options = {}) {
|
|
|
314
395
|
console.log(readPackageVersion());
|
|
315
396
|
return;
|
|
316
397
|
}
|
|
398
|
+
|
|
399
|
+
// Config precedence: CLI args > project config > global config (~/.projectcli.json)
|
|
400
|
+
const userConfig = loadConfig();
|
|
401
|
+
const projectConfigInfo = loadProjectConfig(process.cwd());
|
|
402
|
+
const projectConfig = projectConfigInfo.data || {};
|
|
403
|
+
const effectiveConfig = { ...userConfig, ...projectConfig };
|
|
404
|
+
|
|
317
405
|
if (args.list) {
|
|
318
|
-
printList();
|
|
406
|
+
printList(effectiveConfig);
|
|
319
407
|
return;
|
|
320
408
|
}
|
|
321
409
|
|
|
322
410
|
if (cmd === "add") {
|
|
323
|
-
await runAdd({ prompt, argv: rest });
|
|
411
|
+
await runAdd({ prompt, argv: rest, effectiveConfig });
|
|
324
412
|
return;
|
|
325
413
|
}
|
|
326
414
|
|
|
@@ -329,6 +417,26 @@ async function main(options = {}) {
|
|
|
329
417
|
return;
|
|
330
418
|
}
|
|
331
419
|
|
|
420
|
+
if (cmd === "doctor") {
|
|
421
|
+
await runDoctor({ prompt, argv: rest, effectiveConfig });
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (cmd === "preset") {
|
|
426
|
+
await runPreset({ prompt, argv: rest, effectiveConfig });
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (cmd === "upgrade") {
|
|
431
|
+
await runUpgrade({ prompt, argv: rest });
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (cmd === "plugin") {
|
|
436
|
+
await runPlugin({ prompt, argv: rest, effectiveConfig });
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
332
440
|
// Smart Context Detection
|
|
333
441
|
if (
|
|
334
442
|
cmd === "init" &&
|
|
@@ -361,6 +469,8 @@ async function main(options = {}) {
|
|
|
361
469
|
{ name: "Add Library / Dependency", value: "add" },
|
|
362
470
|
{ name: "Add GitHub Actions CI", value: "ci" },
|
|
363
471
|
{ name: "Add Dockerfile", value: "docker" },
|
|
472
|
+
{ name: "Add Dev Container (VS Code)", value: "devcontainer" },
|
|
473
|
+
{ name: "Add License", value: "license" },
|
|
364
474
|
new inquirer.Separator(),
|
|
365
475
|
{ name: "Start New Project Here", value: "new" },
|
|
366
476
|
{ name: "Exit", value: "exit" },
|
|
@@ -376,20 +486,31 @@ async function main(options = {}) {
|
|
|
376
486
|
return;
|
|
377
487
|
}
|
|
378
488
|
|
|
379
|
-
if (action === "ci" || action === "docker") {
|
|
489
|
+
if (action === "ci" || action === "docker" || action === "devcontainer") {
|
|
380
490
|
const pm = detectPackageManager(process.cwd());
|
|
381
491
|
let langArg = detected;
|
|
382
492
|
if (detected === "JavaScript/TypeScript") langArg = "JavaScript";
|
|
383
493
|
if (detected === "Java/Kotlin") langArg = "Java";
|
|
384
494
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
495
|
+
let steps = [];
|
|
496
|
+
if (action === "ci") steps = generateCI(process.cwd(), langArg, pm);
|
|
497
|
+
else if (action === "docker")
|
|
498
|
+
steps = generateDocker(process.cwd(), langArg);
|
|
499
|
+
else steps = generateDevContainer(process.cwd(), langArg);
|
|
500
|
+
|
|
501
|
+
const { kept, skipped } = filterExistingWriteFiles(
|
|
502
|
+
steps,
|
|
503
|
+
process.cwd()
|
|
504
|
+
);
|
|
389
505
|
|
|
390
|
-
if (
|
|
506
|
+
if (kept.length > 0) {
|
|
391
507
|
console.log("\nApplying changes...");
|
|
392
|
-
await runSteps(
|
|
508
|
+
await runSteps(kept, { projectRoot: process.cwd() });
|
|
509
|
+
if (skipped.length > 0) {
|
|
510
|
+
console.log(
|
|
511
|
+
chalk.dim(`Skipped existing files: ${skipped.join(", ")}`)
|
|
512
|
+
);
|
|
513
|
+
}
|
|
393
514
|
console.log(chalk.green("Done!"));
|
|
394
515
|
} else {
|
|
395
516
|
console.log(
|
|
@@ -401,6 +522,40 @@ async function main(options = {}) {
|
|
|
401
522
|
return;
|
|
402
523
|
}
|
|
403
524
|
|
|
525
|
+
if (action === "license") {
|
|
526
|
+
const { type, author } = await prompt([
|
|
527
|
+
{
|
|
528
|
+
type: "list",
|
|
529
|
+
name: "type",
|
|
530
|
+
message: "Choose a license:",
|
|
531
|
+
choices: licenseTypes,
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
type: "input",
|
|
535
|
+
name: "author",
|
|
536
|
+
message: "Author Name:",
|
|
537
|
+
default: effectiveConfig.author || "The Authors",
|
|
538
|
+
},
|
|
539
|
+
]);
|
|
540
|
+
const steps = generateLicense(process.cwd(), type, author);
|
|
541
|
+
const { kept, skipped } = filterExistingWriteFiles(
|
|
542
|
+
steps,
|
|
543
|
+
process.cwd()
|
|
544
|
+
);
|
|
545
|
+
if (kept.length === 0) {
|
|
546
|
+
console.log(chalk.dim("Nothing to do (LICENSE already exists)."));
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
await runSteps(kept, { projectRoot: process.cwd() });
|
|
550
|
+
if (skipped.length > 0) {
|
|
551
|
+
console.log(
|
|
552
|
+
chalk.dim(`Skipped existing files: ${skipped.join(", ")}`)
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
console.log(chalk.green("Done!"));
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
404
559
|
// If 'new', fall through to normal wizard
|
|
405
560
|
}
|
|
406
561
|
}
|
|
@@ -422,29 +577,80 @@ async function main(options = {}) {
|
|
|
422
577
|
chalk.dim(" (Type to search)")
|
|
423
578
|
);
|
|
424
579
|
|
|
425
|
-
const languages = getLanguages();
|
|
580
|
+
const languages = getLanguages(effectiveConfig);
|
|
426
581
|
if (languages.length === 0) {
|
|
427
582
|
throw new Error("No languages configured.");
|
|
428
583
|
}
|
|
429
584
|
|
|
430
|
-
const userConfig = loadConfig();
|
|
431
|
-
|
|
432
585
|
const allowedPms = ["npm", "pnpm", "yarn", "bun"];
|
|
433
586
|
let preselectedPm =
|
|
434
587
|
typeof args.pm === "string" && allowedPms.includes(args.pm)
|
|
435
588
|
? args.pm
|
|
436
589
|
: undefined;
|
|
437
590
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
591
|
+
const configuredPm =
|
|
592
|
+
typeof effectiveConfig.packageManager === "string"
|
|
593
|
+
? effectiveConfig.packageManager
|
|
594
|
+
: undefined;
|
|
595
|
+
|
|
596
|
+
if (!preselectedPm && configuredPm) {
|
|
597
|
+
if (allowedPms.includes(configuredPm)) {
|
|
598
|
+
preselectedPm = configuredPm;
|
|
441
599
|
}
|
|
442
600
|
}
|
|
443
601
|
|
|
444
|
-
|
|
602
|
+
const presetId =
|
|
603
|
+
typeof effectiveConfig.preset === "string" && effectiveConfig.preset.trim()
|
|
604
|
+
? effectiveConfig.preset.trim()
|
|
605
|
+
: "startup";
|
|
606
|
+
const preset = getPreset(presetId, effectiveConfig);
|
|
607
|
+
const presetPm =
|
|
608
|
+
typeof preset?.defaults?.packageManager === "string"
|
|
609
|
+
? preset.defaults.packageManager
|
|
610
|
+
: null;
|
|
611
|
+
|
|
612
|
+
if (!preselectedPm && presetPm && allowedPms.includes(presetPm)) {
|
|
613
|
+
preselectedPm = presetPm;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const learningEnabled =
|
|
617
|
+
typeof effectiveConfig.learningMode === "boolean"
|
|
618
|
+
? effectiveConfig.learningMode
|
|
619
|
+
: typeof effectiveConfig.learning === "boolean"
|
|
620
|
+
? effectiveConfig.learning
|
|
621
|
+
: false;
|
|
622
|
+
|
|
623
|
+
if (learningEnabled && args.learning === false) {
|
|
445
624
|
args.learning = true;
|
|
446
625
|
}
|
|
447
626
|
|
|
627
|
+
const defaultLicenseType =
|
|
628
|
+
typeof effectiveConfig.license === "string" &&
|
|
629
|
+
licenseTypes.includes(effectiveConfig.license)
|
|
630
|
+
? effectiveConfig.license
|
|
631
|
+
: typeof effectiveConfig.defaultLicense === "string" &&
|
|
632
|
+
licenseTypes.includes(effectiveConfig.defaultLicense)
|
|
633
|
+
? effectiveConfig.defaultLicense
|
|
634
|
+
: null;
|
|
635
|
+
|
|
636
|
+
const defaultAuthor =
|
|
637
|
+
typeof effectiveConfig.author === "string" && effectiveConfig.author.trim()
|
|
638
|
+
? effectiveConfig.author.trim()
|
|
639
|
+
: "The Authors";
|
|
640
|
+
|
|
641
|
+
const defaultCi =
|
|
642
|
+
typeof effectiveConfig.ci === "boolean"
|
|
643
|
+
? effectiveConfig.ci
|
|
644
|
+
: preset?.defaults?.extras?.ci === true;
|
|
645
|
+
const defaultDocker =
|
|
646
|
+
typeof effectiveConfig.docker === "boolean"
|
|
647
|
+
? effectiveConfig.docker
|
|
648
|
+
: preset?.defaults?.extras?.docker === true;
|
|
649
|
+
const defaultDevContainer =
|
|
650
|
+
typeof effectiveConfig.devcontainer === "boolean"
|
|
651
|
+
? effectiveConfig.devcontainer
|
|
652
|
+
: preset?.defaults?.extras?.devcontainer === true;
|
|
653
|
+
|
|
448
654
|
const state = {
|
|
449
655
|
template: args.template || undefined,
|
|
450
656
|
language:
|
|
@@ -457,7 +663,10 @@ async function main(options = {}) {
|
|
|
457
663
|
};
|
|
458
664
|
|
|
459
665
|
if (state.language) {
|
|
460
|
-
const frameworksForLanguage = getFrameworks(
|
|
666
|
+
const frameworksForLanguage = getFrameworks(
|
|
667
|
+
state.language,
|
|
668
|
+
effectiveConfig
|
|
669
|
+
);
|
|
461
670
|
state.framework =
|
|
462
671
|
args.framework && frameworksForLanguage.includes(args.framework)
|
|
463
672
|
? args.framework
|
|
@@ -480,7 +689,7 @@ async function main(options = {}) {
|
|
|
480
689
|
while (true) {
|
|
481
690
|
if (step === "language") {
|
|
482
691
|
const languageChoices = languages.map((lang) => {
|
|
483
|
-
const count = getFrameworks(lang).length;
|
|
692
|
+
const count = getFrameworks(lang, effectiveConfig).length;
|
|
484
693
|
return { name: `${lang} (${count})`, value: lang, short: lang };
|
|
485
694
|
});
|
|
486
695
|
|
|
@@ -519,15 +728,19 @@ async function main(options = {}) {
|
|
|
519
728
|
}
|
|
520
729
|
|
|
521
730
|
if (step === "framework") {
|
|
522
|
-
const frameworks = getFrameworks(state.language);
|
|
731
|
+
const frameworks = getFrameworks(state.language, effectiveConfig);
|
|
523
732
|
if (frameworks.length === 0) {
|
|
524
733
|
throw new Error(`No frameworks configured for ${state.language}.`);
|
|
525
734
|
}
|
|
526
735
|
|
|
527
736
|
const frameworkChoices = frameworks.map((fw) => {
|
|
528
|
-
const gen = getGenerator(state.language, fw);
|
|
737
|
+
const gen = getGenerator(state.language, fw, effectiveConfig);
|
|
529
738
|
const note = gen?.notes ? ` — ${gen.notes}` : "";
|
|
530
|
-
|
|
739
|
+
const badge = gen?.stability
|
|
740
|
+
? formatStabilityBadge(gen.stability)
|
|
741
|
+
: null;
|
|
742
|
+
const badgeText = badge ? ` ${badge}` : "";
|
|
743
|
+
return { name: `${fw}${badgeText}${note}`, value: fw, short: fw };
|
|
531
744
|
});
|
|
532
745
|
|
|
533
746
|
const frameworkQuestion = hasAutocomplete
|
|
@@ -566,7 +779,11 @@ async function main(options = {}) {
|
|
|
566
779
|
state.framework = answer.framework;
|
|
567
780
|
|
|
568
781
|
// Preflight Checks
|
|
569
|
-
const gen = getGenerator(
|
|
782
|
+
const gen = getGenerator(
|
|
783
|
+
state.language,
|
|
784
|
+
state.framework,
|
|
785
|
+
effectiveConfig
|
|
786
|
+
);
|
|
570
787
|
if (gen && gen.check && gen.check.length > 0) {
|
|
571
788
|
/* eslint-disable-next-line no-console */
|
|
572
789
|
console.log(chalk.dim("\n(checking requirements...)"));
|
|
@@ -575,7 +792,7 @@ async function main(options = {}) {
|
|
|
575
792
|
|
|
576
793
|
if (missing.length > 0) {
|
|
577
794
|
console.log(chalk.red.bold("\nMissing required tools:"));
|
|
578
|
-
missing.forEach((m) =>
|
|
795
|
+
missing.forEach((m) => printMissingTool(m.bin));
|
|
579
796
|
console.log(
|
|
580
797
|
chalk.yellow("You may not be able to build or run this project.\n")
|
|
581
798
|
);
|
|
@@ -737,6 +954,105 @@ async function main(options = {}) {
|
|
|
737
954
|
// Remove .git to make it a fresh project
|
|
738
955
|
removeGitFolder(projectRoot);
|
|
739
956
|
|
|
957
|
+
// Optional extras after cloning
|
|
958
|
+
const detectedTemplate = detectLanguage(projectRoot);
|
|
959
|
+
const pmTemplate = detectPackageManager(projectRoot);
|
|
960
|
+
|
|
961
|
+
let langArg = detectedTemplate;
|
|
962
|
+
if (detectedTemplate === "JavaScript/TypeScript")
|
|
963
|
+
langArg = "JavaScript";
|
|
964
|
+
if (detectedTemplate === "Java/Kotlin") langArg = "Java";
|
|
965
|
+
|
|
966
|
+
let wantCi = Boolean(args.ci) || defaultCi;
|
|
967
|
+
let wantDocker = Boolean(args.docker) || defaultDocker;
|
|
968
|
+
let wantDevContainer =
|
|
969
|
+
Boolean(args.devcontainer) || defaultDevContainer;
|
|
970
|
+
let wantLicense =
|
|
971
|
+
typeof args.license === "boolean"
|
|
972
|
+
? args.license
|
|
973
|
+
: defaultLicenseType !== null;
|
|
974
|
+
|
|
975
|
+
if (!args.yes) {
|
|
976
|
+
const licenseLabel =
|
|
977
|
+
defaultLicenseType !== null
|
|
978
|
+
? `LICENSE (${defaultLicenseType})`
|
|
979
|
+
: "LICENSE (skip)";
|
|
980
|
+
|
|
981
|
+
const { extras } = await prompt([
|
|
982
|
+
{
|
|
983
|
+
type: "checkbox",
|
|
984
|
+
name: "extras",
|
|
985
|
+
message: "Extras to apply after clone:",
|
|
986
|
+
choices: [
|
|
987
|
+
{ name: "GitHub Actions CI", value: "ci", checked: wantCi },
|
|
988
|
+
{
|
|
989
|
+
name: "Dockerfile",
|
|
990
|
+
value: "docker",
|
|
991
|
+
checked: wantDocker,
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
name: "Dev Container (VS Code)",
|
|
995
|
+
value: "devcontainer",
|
|
996
|
+
checked: wantDevContainer,
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
name: licenseLabel,
|
|
1000
|
+
value: "license",
|
|
1001
|
+
checked: wantLicense,
|
|
1002
|
+
disabled:
|
|
1003
|
+
defaultLicenseType === null
|
|
1004
|
+
? "Configure a default license in 'projectcli config'"
|
|
1005
|
+
: false,
|
|
1006
|
+
},
|
|
1007
|
+
],
|
|
1008
|
+
},
|
|
1009
|
+
]);
|
|
1010
|
+
if (extras) {
|
|
1011
|
+
wantCi = extras.includes("ci");
|
|
1012
|
+
wantDocker = extras.includes("docker");
|
|
1013
|
+
wantDevContainer = extras.includes("devcontainer");
|
|
1014
|
+
wantLicense = extras.includes("license");
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
const extraSteps = [];
|
|
1019
|
+
if (wantCi)
|
|
1020
|
+
extraSteps.push(...generateCI(projectRoot, langArg, pmTemplate));
|
|
1021
|
+
if (wantDocker)
|
|
1022
|
+
extraSteps.push(...generateDocker(projectRoot, langArg));
|
|
1023
|
+
if (wantDevContainer) {
|
|
1024
|
+
extraSteps.push(...generateDevContainer(projectRoot, langArg));
|
|
1025
|
+
}
|
|
1026
|
+
if (wantLicense && defaultLicenseType !== null) {
|
|
1027
|
+
extraSteps.push(
|
|
1028
|
+
...generateLicense(projectRoot, defaultLicenseType, defaultAuthor)
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
if (wantLicense && defaultLicenseType === null) {
|
|
1033
|
+
console.log(
|
|
1034
|
+
chalk.dim(
|
|
1035
|
+
"Skipping LICENSE (no default license configured; run 'projectcli config')."
|
|
1036
|
+
)
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
if (extraSteps.length > 0) {
|
|
1041
|
+
const { kept, skipped } = filterExistingWriteFiles(
|
|
1042
|
+
extraSteps,
|
|
1043
|
+
projectRoot
|
|
1044
|
+
);
|
|
1045
|
+
if (kept.length > 0) {
|
|
1046
|
+
console.log(chalk.dim("\nApplying extras..."));
|
|
1047
|
+
await runSteps(kept, { projectRoot });
|
|
1048
|
+
}
|
|
1049
|
+
if (skipped.length > 0) {
|
|
1050
|
+
console.log(
|
|
1051
|
+
chalk.dim(`Skipped existing files: ${skipped.join(", ")}`)
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
740
1056
|
console.log(
|
|
741
1057
|
chalk.green(`\nSuccess! Created project at ${projectRoot}`)
|
|
742
1058
|
);
|
|
@@ -746,7 +1062,14 @@ async function main(options = {}) {
|
|
|
746
1062
|
)
|
|
747
1063
|
);
|
|
748
1064
|
} catch (err) {
|
|
749
|
-
|
|
1065
|
+
const message = err && err.message ? err.message : String(err);
|
|
1066
|
+
console.error(chalk.red("\nFailed to clone template:"), message);
|
|
1067
|
+
if (
|
|
1068
|
+
/\bENOENT\b/i.test(message) ||
|
|
1069
|
+
/command not found/i.test(message)
|
|
1070
|
+
) {
|
|
1071
|
+
printMissingTool("git");
|
|
1072
|
+
}
|
|
750
1073
|
process.exit(1);
|
|
751
1074
|
}
|
|
752
1075
|
return;
|
|
@@ -838,6 +1161,11 @@ async function main(options = {}) {
|
|
|
838
1161
|
const message = err && err.message ? err.message : String(err);
|
|
839
1162
|
console.error(`\nError: ${message}`);
|
|
840
1163
|
|
|
1164
|
+
const cmdNotFound = /^Command not found:\s*(.+)$/.exec(message);
|
|
1165
|
+
if (cmdNotFound && cmdNotFound[1]) {
|
|
1166
|
+
printMissingTool(cmdNotFound[1].trim());
|
|
1167
|
+
}
|
|
1168
|
+
|
|
841
1169
|
const looksLikeNameIssue =
|
|
842
1170
|
/cannot be used as a package name|Rust keyword|keyword|Cargo\.toml/i.test(
|
|
843
1171
|
message
|
|
@@ -921,10 +1249,16 @@ async function main(options = {}) {
|
|
|
921
1249
|
}
|
|
922
1250
|
}
|
|
923
1251
|
|
|
924
|
-
// CI/CD & Docker
|
|
1252
|
+
// CI/CD & Docker & DevContainer
|
|
925
1253
|
if (!args.dryRun) {
|
|
926
|
-
let wantCi = args.ci;
|
|
927
|
-
let wantDocker = args.docker;
|
|
1254
|
+
let wantCi = Boolean(args.ci) || defaultCi;
|
|
1255
|
+
let wantDocker = Boolean(args.docker) || defaultDocker;
|
|
1256
|
+
let wantDevContainer =
|
|
1257
|
+
Boolean(args.devcontainer) || defaultDevContainer;
|
|
1258
|
+
let wantLicense =
|
|
1259
|
+
typeof args.license === "boolean"
|
|
1260
|
+
? args.license
|
|
1261
|
+
: defaultLicenseType !== null;
|
|
928
1262
|
|
|
929
1263
|
if (!args.yes) {
|
|
930
1264
|
const { extras } = await prompt([
|
|
@@ -935,12 +1269,31 @@ async function main(options = {}) {
|
|
|
935
1269
|
choices: [
|
|
936
1270
|
{ name: "GitHub Actions CI", value: "ci", checked: wantCi },
|
|
937
1271
|
{ name: "Dockerfile", value: "docker", checked: wantDocker },
|
|
1272
|
+
{
|
|
1273
|
+
name: "Dev Container (VS Code)",
|
|
1274
|
+
value: "devcontainer",
|
|
1275
|
+
checked: wantDevContainer,
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
name:
|
|
1279
|
+
defaultLicenseType !== null
|
|
1280
|
+
? `LICENSE (${defaultLicenseType})`
|
|
1281
|
+
: "LICENSE (skip)",
|
|
1282
|
+
value: "license",
|
|
1283
|
+
checked: wantLicense,
|
|
1284
|
+
disabled:
|
|
1285
|
+
defaultLicenseType === null
|
|
1286
|
+
? "Configure a default license in 'projectcli config'"
|
|
1287
|
+
: false,
|
|
1288
|
+
},
|
|
938
1289
|
],
|
|
939
1290
|
},
|
|
940
1291
|
]);
|
|
941
1292
|
if (extras) {
|
|
942
1293
|
wantCi = extras.includes("ci");
|
|
943
1294
|
wantDocker = extras.includes("docker");
|
|
1295
|
+
wantDevContainer = extras.includes("devcontainer");
|
|
1296
|
+
wantLicense = extras.includes("license");
|
|
944
1297
|
}
|
|
945
1298
|
}
|
|
946
1299
|
|
|
@@ -951,10 +1304,28 @@ async function main(options = {}) {
|
|
|
951
1304
|
if (wantDocker) {
|
|
952
1305
|
extraSteps.push(...generateDocker(projectRoot, state.language));
|
|
953
1306
|
}
|
|
1307
|
+
if (wantDevContainer) {
|
|
1308
|
+
extraSteps.push(...generateDevContainer(projectRoot, state.language));
|
|
1309
|
+
}
|
|
1310
|
+
if (wantLicense && defaultLicenseType !== null) {
|
|
1311
|
+
extraSteps.push(
|
|
1312
|
+
...generateLicense(projectRoot, defaultLicenseType, defaultAuthor)
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
954
1315
|
|
|
955
|
-
|
|
1316
|
+
const { kept, skipped } = filterExistingWriteFiles(
|
|
1317
|
+
extraSteps,
|
|
1318
|
+
projectRoot
|
|
1319
|
+
);
|
|
1320
|
+
|
|
1321
|
+
if (kept.length > 0) {
|
|
956
1322
|
console.log("Adding extras...");
|
|
957
|
-
await runSteps(
|
|
1323
|
+
await runSteps(kept, { projectRoot });
|
|
1324
|
+
}
|
|
1325
|
+
if (skipped.length > 0) {
|
|
1326
|
+
console.log(
|
|
1327
|
+
chalk.dim(`Skipped existing files: ${skipped.join(", ")}`)
|
|
1328
|
+
);
|
|
958
1329
|
}
|
|
959
1330
|
}
|
|
960
1331
|
|