@devobsessed/code-captain 0.4.0 → 0.4.1
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/install.js +62 -11
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -444,16 +444,44 @@ class CodeCaptainInstaller {
|
|
|
444
444
|
return recommendations;
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
// Detect .sln files in the current directory
|
|
447
|
+
// Detect .sln / .slnx files in the current directory
|
|
448
448
|
async detectSlnFiles() {
|
|
449
449
|
try {
|
|
450
450
|
const entries = await fs.readdir(".");
|
|
451
|
-
return entries.filter(
|
|
451
|
+
return entries.filter(
|
|
452
|
+
(entry) => entry.endsWith(".sln") || entry.endsWith(".slnx")
|
|
453
|
+
);
|
|
452
454
|
} catch {
|
|
453
455
|
return [];
|
|
454
456
|
}
|
|
455
457
|
}
|
|
456
458
|
|
|
459
|
+
// Add code-captain.csproj to an XML-format .slnx file
|
|
460
|
+
async addProjectToSlnx(slnxPath, csprojFileName) {
|
|
461
|
+
const content = await fs.readFile(slnxPath, "utf8");
|
|
462
|
+
const idMarker = `Path="${csprojFileName}"`;
|
|
463
|
+
|
|
464
|
+
if (content.includes(idMarker)) return "up-to-date";
|
|
465
|
+
|
|
466
|
+
// Detect line ending style
|
|
467
|
+
const nl = content.includes("\r\n") ? "\r\n" : "\n";
|
|
468
|
+
|
|
469
|
+
const updated = content.replace(
|
|
470
|
+
/<\/Solution>/,
|
|
471
|
+
` <Project Path="${csprojFileName}" />${nl}</Solution>`
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
if (updated === content) {
|
|
475
|
+
throw new Error(
|
|
476
|
+
`Could not find </Solution> in ${slnxPath}. Please add code-captain.csproj to your solution manually in Visual Studio.`
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
await fs.copy(slnxPath, `${slnxPath}.backup`);
|
|
481
|
+
await fs.writeFile(slnxPath, updated);
|
|
482
|
+
return "updated";
|
|
483
|
+
}
|
|
484
|
+
|
|
457
485
|
// Generate a random GUID (uppercase)
|
|
458
486
|
generateGuid() {
|
|
459
487
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
|
|
@@ -472,15 +500,33 @@ class CodeCaptainInstaller {
|
|
|
472
500
|
|
|
473
501
|
if (content.includes(idMarker)) return "up-to-date";
|
|
474
502
|
|
|
503
|
+
// Detect line ending style used by the .sln file
|
|
504
|
+
const nl = content.includes("\r\n") ? "\r\n" : "\n";
|
|
505
|
+
|
|
475
506
|
const projectGuid = this.generateGuid();
|
|
476
507
|
// SDK-style C# project type GUID
|
|
477
508
|
const typeGuid = "{9A19103F-16F7-4668-BE54-9A1E7A4F7556}";
|
|
478
509
|
const projectBlock =
|
|
479
|
-
`Project("${typeGuid}") = "${projectName}", "${csprojFileName}", "{${projectGuid}}"
|
|
480
|
-
`EndProject
|
|
510
|
+
`Project("${typeGuid}") = "${projectName}", "${csprojFileName}", "{${projectGuid}}"${nl}` +
|
|
511
|
+
`EndProject${nl}`;
|
|
512
|
+
|
|
513
|
+
// Find the last EndProject and insert after it (more reliable than searching for Global)
|
|
514
|
+
const lastEndProjectIdx = content.lastIndexOf(`EndProject${nl}`);
|
|
515
|
+
let updated;
|
|
516
|
+
if (lastEndProjectIdx !== -1) {
|
|
517
|
+
const insertAt = lastEndProjectIdx + `EndProject${nl}`.length;
|
|
518
|
+
updated = content.slice(0, insertAt) + projectBlock + content.slice(insertAt);
|
|
519
|
+
} else {
|
|
520
|
+
// Fallback: insert before Global section
|
|
521
|
+
updated = content.replace(/^(Global\r?\n)/m, `${projectBlock}$1`);
|
|
522
|
+
}
|
|
481
523
|
|
|
482
|
-
|
|
483
|
-
|
|
524
|
+
if (updated === content) {
|
|
525
|
+
// Regex fallback also failed — .sln format not recognized
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Could not find insertion point in ${slnPath}. Please add code-captain.csproj to your solution manually in Visual Studio.`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
484
530
|
|
|
485
531
|
// Parse solution configs and add ActiveCfg entries (but not Build.0, so it's excluded from builds)
|
|
486
532
|
const configSectionMatch = content.match(
|
|
@@ -496,11 +542,11 @@ class CodeCaptainInstaller {
|
|
|
496
542
|
if (configNames.length > 0) {
|
|
497
543
|
const activeCfgEntries = configNames
|
|
498
544
|
.map((c) => `\t\t{${projectGuid}}.${c}.ActiveCfg = ${c}`)
|
|
499
|
-
.join(
|
|
545
|
+
.join(nl);
|
|
500
546
|
|
|
501
547
|
updated = updated.replace(
|
|
502
548
|
/(GlobalSection\(ProjectConfigurationPlatforms\)[^\n]*\n)([\s\S]*?)(EndGlobalSection)/,
|
|
503
|
-
`$1$2${activeCfgEntries}\
|
|
549
|
+
`$1$2${activeCfgEntries}${nl}\t$3`
|
|
504
550
|
);
|
|
505
551
|
}
|
|
506
552
|
}
|
|
@@ -591,11 +637,16 @@ class CodeCaptainInstaller {
|
|
|
591
637
|
}
|
|
592
638
|
}
|
|
593
639
|
|
|
594
|
-
// Register in .sln
|
|
640
|
+
// Register in .sln / .slnx
|
|
595
641
|
const slnFiles = await this.detectSlnFiles();
|
|
596
642
|
let slnAction = "no-sln";
|
|
597
643
|
if (slnFiles.length > 0) {
|
|
598
|
-
|
|
644
|
+
const slnFile = slnFiles[0];
|
|
645
|
+
if (slnFile.endsWith(".slnx")) {
|
|
646
|
+
slnAction = await this.addProjectToSlnx(slnFile, targetPath);
|
|
647
|
+
} else {
|
|
648
|
+
slnAction = await this.addProjectToSln(slnFile, targetPath);
|
|
649
|
+
}
|
|
599
650
|
}
|
|
600
651
|
|
|
601
652
|
// Migrate: remove Code Captain section from Directory.Build.props if present
|
|
@@ -1466,7 +1517,7 @@ class CodeCaptainInstaller {
|
|
|
1466
1517
|
const slnMessages = {
|
|
1467
1518
|
updated: "registered in solution (.sln backup saved as .backup)",
|
|
1468
1519
|
"up-to-date": "already registered in solution",
|
|
1469
|
-
"no-sln": "no .sln file found — add code-captain.csproj to your solution manually",
|
|
1520
|
+
"no-sln": "no .sln/.slnx file found — add code-captain.csproj to your solution manually in Visual Studio",
|
|
1470
1521
|
};
|
|
1471
1522
|
let message = csprojMessages[csprojAction] || csprojAction;
|
|
1472
1523
|
if (slnMessages[slnAction]) message += ` — ${slnMessages[slnAction]}`;
|