create-nexu 1.1.0 → 1.1.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/dist/index.js +392 -124
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -90,18 +90,6 @@ function execInherit(command, cwd) {
|
|
|
90
90
|
throw new Error(`Command failed: ${command}`);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
function copyWithMerge(src, dest, overwrite = false) {
|
|
94
|
-
if (fs.existsSync(dest) && !overwrite) {
|
|
95
|
-
if (fs.statSync(src).isDirectory() && fs.statSync(dest).isDirectory()) {
|
|
96
|
-
const files = fs.readdirSync(src);
|
|
97
|
-
for (const file of files) {
|
|
98
|
-
copyWithMerge(path.join(src, file), path.join(dest, file), overwrite);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
fs.copySync(src, dest, { overwrite });
|
|
104
|
-
}
|
|
105
93
|
function log(message, type = "info") {
|
|
106
94
|
const colors = {
|
|
107
95
|
info: chalk.blue,
|
|
@@ -517,6 +505,7 @@ async function init(projectName, options) {
|
|
|
517
505
|
import path4 from "path";
|
|
518
506
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
519
507
|
import chalk4 from "chalk";
|
|
508
|
+
import { diffLines } from "diff";
|
|
520
509
|
import fs4 from "fs-extra";
|
|
521
510
|
import inquirer3 from "inquirer";
|
|
522
511
|
import ora3 from "ora";
|
|
@@ -534,6 +523,138 @@ function getTemplateDir3() {
|
|
|
534
523
|
}
|
|
535
524
|
throw new Error("Template directory not found. Please reinstall the package.");
|
|
536
525
|
}
|
|
526
|
+
function compareFiles(srcPath, destPath) {
|
|
527
|
+
if (!fs4.existsSync(destPath)) return false;
|
|
528
|
+
if (!fs4.existsSync(srcPath)) return false;
|
|
529
|
+
const srcStat = fs4.statSync(srcPath);
|
|
530
|
+
const destStat = fs4.statSync(destPath);
|
|
531
|
+
if (srcStat.isDirectory() || destStat.isDirectory()) return false;
|
|
532
|
+
const srcContent = fs4.readFileSync(srcPath, "utf-8");
|
|
533
|
+
const destContent = fs4.readFileSync(destPath, "utf-8");
|
|
534
|
+
return srcContent === destContent;
|
|
535
|
+
}
|
|
536
|
+
function collectFileChanges(srcDir, destDir, category, basePath = "") {
|
|
537
|
+
const changes = [];
|
|
538
|
+
if (!fs4.existsSync(srcDir)) return changes;
|
|
539
|
+
const entries = fs4.readdirSync(srcDir, { withFileTypes: true });
|
|
540
|
+
for (const entry of entries) {
|
|
541
|
+
const srcPath = path4.join(srcDir, entry.name);
|
|
542
|
+
const destPath = path4.join(destDir, entry.name);
|
|
543
|
+
const relativePath = basePath ? path4.join(basePath, entry.name) : entry.name;
|
|
544
|
+
if (entry.isDirectory()) {
|
|
545
|
+
changes.push(...collectFileChanges(srcPath, destPath, category, relativePath));
|
|
546
|
+
} else {
|
|
547
|
+
if (!fs4.existsSync(destPath)) {
|
|
548
|
+
changes.push({ type: "add", relativePath, srcPath, destPath, category });
|
|
549
|
+
} else if (!compareFiles(srcPath, destPath)) {
|
|
550
|
+
changes.push({ type: "modify", relativePath, srcPath, destPath, category });
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return changes;
|
|
555
|
+
}
|
|
556
|
+
function showFileDiff(srcPath, destPath) {
|
|
557
|
+
if (!fs4.existsSync(destPath)) {
|
|
558
|
+
console.log(chalk4.green(" (new file)"));
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const srcContent = fs4.readFileSync(srcPath, "utf-8");
|
|
562
|
+
const destContent = fs4.readFileSync(destPath, "utf-8");
|
|
563
|
+
const differences = diffLines(destContent, srcContent);
|
|
564
|
+
let hasChanges2 = false;
|
|
565
|
+
for (const part of differences) {
|
|
566
|
+
if (part.added || part.removed) {
|
|
567
|
+
hasChanges2 = true;
|
|
568
|
+
const lines = part.value.split("\n").filter((l) => l.length > 0);
|
|
569
|
+
for (const line of lines.slice(0, 5)) {
|
|
570
|
+
if (part.added) {
|
|
571
|
+
console.log(chalk4.green(` + ${line.substring(0, 80)}`));
|
|
572
|
+
} else {
|
|
573
|
+
console.log(chalk4.red(` - ${line.substring(0, 80)}`));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (lines.length > 5) {
|
|
577
|
+
console.log(chalk4.gray(` ... and ${lines.length - 5} more lines`));
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (!hasChanges2) {
|
|
582
|
+
console.log(chalk4.gray(" (no visible changes)"));
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function collectDependencyChanges(templatePkgPath, projectPkgPath) {
|
|
586
|
+
if (!fs4.existsSync(templatePkgPath) || !fs4.existsSync(projectPkgPath)) {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
const templatePkg = fs4.readJsonSync(templatePkgPath);
|
|
590
|
+
const projectPkg = fs4.readJsonSync(projectPkgPath);
|
|
591
|
+
const changes = {
|
|
592
|
+
type: "package.json",
|
|
593
|
+
changes: {
|
|
594
|
+
added: { dependencies: {}, devDependencies: {}, scripts: {} },
|
|
595
|
+
updated: { dependencies: {}, devDependencies: {}, scripts: {} }
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
for (const depType of ["dependencies", "devDependencies"]) {
|
|
599
|
+
if (templatePkg[depType]) {
|
|
600
|
+
for (const [pkg, version] of Object.entries(templatePkg[depType])) {
|
|
601
|
+
if (!projectPkg[depType]?.[pkg]) {
|
|
602
|
+
changes.changes.added[depType][pkg] = version;
|
|
603
|
+
} else if (projectPkg[depType][pkg] !== version) {
|
|
604
|
+
changes.changes.updated[depType][pkg] = {
|
|
605
|
+
from: projectPkg[depType][pkg],
|
|
606
|
+
to: version
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
if (templatePkg.scripts) {
|
|
613
|
+
for (const [script, cmd] of Object.entries(templatePkg.scripts)) {
|
|
614
|
+
if (!projectPkg.scripts?.[script]) {
|
|
615
|
+
changes.changes.added.scripts[script] = cmd;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return changes;
|
|
620
|
+
}
|
|
621
|
+
function displayDependencyChanges(changes) {
|
|
622
|
+
const { added, updated } = changes.changes;
|
|
623
|
+
if (Object.keys(added.dependencies).length > 0) {
|
|
624
|
+
console.log(chalk4.cyan("\n New dependencies:"));
|
|
625
|
+
for (const [pkg, version] of Object.entries(added.dependencies)) {
|
|
626
|
+
console.log(chalk4.green(` + ${pkg}: ${version}`));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (Object.keys(added.devDependencies).length > 0) {
|
|
630
|
+
console.log(chalk4.cyan("\n New devDependencies:"));
|
|
631
|
+
for (const [pkg, version] of Object.entries(added.devDependencies)) {
|
|
632
|
+
console.log(chalk4.green(` + ${pkg}: ${version}`));
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
if (Object.keys(updated.dependencies).length > 0) {
|
|
636
|
+
console.log(chalk4.cyan("\n Updated dependencies:"));
|
|
637
|
+
for (const [pkg, { from, to }] of Object.entries(updated.dependencies)) {
|
|
638
|
+
console.log(chalk4.yellow(` ~ ${pkg}: ${from} \u2192 ${to}`));
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (Object.keys(updated.devDependencies).length > 0) {
|
|
642
|
+
console.log(chalk4.cyan("\n Updated devDependencies:"));
|
|
643
|
+
for (const [pkg, { from, to }] of Object.entries(updated.devDependencies)) {
|
|
644
|
+
console.log(chalk4.yellow(` ~ ${pkg}: ${from} \u2192 ${to}`));
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (Object.keys(added.scripts).length > 0) {
|
|
648
|
+
console.log(chalk4.cyan("\n New scripts:"));
|
|
649
|
+
for (const [script, cmd] of Object.entries(added.scripts)) {
|
|
650
|
+
console.log(chalk4.green(` + ${script}: ${String(cmd).substring(0, 50)}...`));
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
function hasChanges(changes) {
|
|
655
|
+
const { added, updated } = changes.changes;
|
|
656
|
+
return Object.keys(added.dependencies).length > 0 || Object.keys(added.devDependencies).length > 0 || Object.keys(added.scripts).length > 0 || Object.keys(updated.dependencies).length > 0 || Object.keys(updated.devDependencies).length > 0;
|
|
657
|
+
}
|
|
537
658
|
async function update(options) {
|
|
538
659
|
console.log(chalk4.bold("\n\u{1F504} Update Nexu Project\n"));
|
|
539
660
|
const projectDir = process.cwd();
|
|
@@ -544,28 +665,227 @@ async function update(options) {
|
|
|
544
665
|
);
|
|
545
666
|
process.exit(1);
|
|
546
667
|
}
|
|
547
|
-
const
|
|
668
|
+
const templateDir = getTemplateDir3();
|
|
669
|
+
const updateAll = options.all || !options.packages && !options.config && !options.workflows && !options.services && !options.scripts && !options.dependencies;
|
|
548
670
|
const updateTargets = {
|
|
549
671
|
packages: updateAll || options.packages,
|
|
550
672
|
config: updateAll || options.config,
|
|
551
673
|
workflows: updateAll || options.workflows,
|
|
552
|
-
services: updateAll || options.services
|
|
674
|
+
services: updateAll || options.services,
|
|
675
|
+
scripts: updateAll || options.scripts,
|
|
676
|
+
dependencies: updateAll || options.dependencies
|
|
553
677
|
};
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
if (updateTargets.
|
|
558
|
-
|
|
678
|
+
const spinner = ora3("Analyzing changes...").start();
|
|
679
|
+
const allFileChanges = [];
|
|
680
|
+
let dependencyChanges = null;
|
|
681
|
+
if (updateTargets.config) {
|
|
682
|
+
for (const file of TEMPLATE_DIRS.config) {
|
|
683
|
+
const srcFile = path4.join(templateDir, file);
|
|
684
|
+
const destFile = path4.join(projectDir, file);
|
|
685
|
+
if (fs4.existsSync(srcFile)) {
|
|
686
|
+
if (!fs4.existsSync(destFile)) {
|
|
687
|
+
allFileChanges.push({
|
|
688
|
+
type: "add",
|
|
689
|
+
relativePath: file,
|
|
690
|
+
srcPath: srcFile,
|
|
691
|
+
destPath: destFile,
|
|
692
|
+
category: "config"
|
|
693
|
+
});
|
|
694
|
+
} else if (!compareFiles(srcFile, destFile)) {
|
|
695
|
+
allFileChanges.push({
|
|
696
|
+
type: "modify",
|
|
697
|
+
relativePath: file,
|
|
698
|
+
srcPath: srcFile,
|
|
699
|
+
destPath: destFile,
|
|
700
|
+
category: "config"
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
allFileChanges.push(
|
|
706
|
+
...collectFileChanges(
|
|
707
|
+
path4.join(templateDir, ".husky"),
|
|
708
|
+
path4.join(projectDir, ".husky"),
|
|
709
|
+
"config",
|
|
710
|
+
".husky"
|
|
711
|
+
)
|
|
712
|
+
);
|
|
713
|
+
allFileChanges.push(
|
|
714
|
+
...collectFileChanges(
|
|
715
|
+
path4.join(templateDir, ".vscode"),
|
|
716
|
+
path4.join(projectDir, ".vscode"),
|
|
717
|
+
"config",
|
|
718
|
+
".vscode"
|
|
719
|
+
)
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
if (updateTargets.workflows) {
|
|
723
|
+
allFileChanges.push(
|
|
724
|
+
...collectFileChanges(
|
|
725
|
+
path4.join(templateDir, ".github"),
|
|
726
|
+
path4.join(projectDir, ".github"),
|
|
727
|
+
"workflows",
|
|
728
|
+
".github"
|
|
729
|
+
)
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
if (updateTargets.services) {
|
|
733
|
+
allFileChanges.push(
|
|
734
|
+
...collectFileChanges(
|
|
735
|
+
path4.join(templateDir, "services"),
|
|
736
|
+
path4.join(projectDir, "services"),
|
|
737
|
+
"services",
|
|
738
|
+
"services"
|
|
739
|
+
)
|
|
740
|
+
);
|
|
741
|
+
allFileChanges.push(
|
|
742
|
+
...collectFileChanges(
|
|
743
|
+
path4.join(templateDir, "docker"),
|
|
744
|
+
path4.join(projectDir, "docker"),
|
|
745
|
+
"services",
|
|
746
|
+
"docker"
|
|
747
|
+
)
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
if (updateTargets.scripts) {
|
|
751
|
+
allFileChanges.push(
|
|
752
|
+
...collectFileChanges(
|
|
753
|
+
path4.join(templateDir, "scripts"),
|
|
754
|
+
path4.join(projectDir, "scripts"),
|
|
755
|
+
"scripts",
|
|
756
|
+
"scripts"
|
|
757
|
+
)
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
if (updateTargets.packages) {
|
|
761
|
+
const existingPackages = fs4.existsSync(path4.join(projectDir, "packages")) ? fs4.readdirSync(path4.join(projectDir, "packages")).filter((f) => fs4.statSync(path4.join(projectDir, "packages", f)).isDirectory()) : [];
|
|
762
|
+
for (const pkg of SHARED_PACKAGES) {
|
|
763
|
+
if (existingPackages.includes(pkg)) {
|
|
764
|
+
allFileChanges.push(
|
|
765
|
+
...collectFileChanges(
|
|
766
|
+
path4.join(templateDir, "packages", pkg),
|
|
767
|
+
path4.join(projectDir, "packages", pkg),
|
|
768
|
+
"packages",
|
|
769
|
+
path4.join("packages", pkg)
|
|
770
|
+
)
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (updateTargets.dependencies) {
|
|
776
|
+
dependencyChanges = collectDependencyChanges(
|
|
777
|
+
path4.join(templateDir, "package.json"),
|
|
778
|
+
path4.join(projectDir, "package.json")
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
spinner.stop();
|
|
782
|
+
const addedFiles = allFileChanges.filter((c) => c.type === "add");
|
|
783
|
+
const modifiedFiles = allFileChanges.filter((c) => c.type === "modify");
|
|
784
|
+
if (addedFiles.length === 0 && modifiedFiles.length === 0 && (!dependencyChanges || !hasChanges(dependencyChanges))) {
|
|
785
|
+
console.log(chalk4.green("\u2713 Your project is up to date! No changes needed.\n"));
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
console.log(chalk4.bold("\u{1F4CB} Changes to apply:\n"));
|
|
789
|
+
const categories = [...new Set(allFileChanges.map((c) => c.category))];
|
|
790
|
+
for (const category of categories) {
|
|
791
|
+
const categoryChanges = allFileChanges.filter((c) => c.category === category);
|
|
792
|
+
if (categoryChanges.length === 0) continue;
|
|
793
|
+
const categoryNames = {
|
|
794
|
+
config: "Configuration files",
|
|
795
|
+
workflows: "GitHub workflows",
|
|
796
|
+
services: "Docker services",
|
|
797
|
+
scripts: "Scripts",
|
|
798
|
+
packages: "Shared packages"
|
|
799
|
+
};
|
|
800
|
+
console.log(chalk4.cyan.bold(`
|
|
801
|
+
${categoryNames[category] || category}:`));
|
|
802
|
+
const added = categoryChanges.filter((c) => c.type === "add");
|
|
803
|
+
const modified = categoryChanges.filter((c) => c.type === "modify");
|
|
804
|
+
if (added.length > 0) {
|
|
805
|
+
console.log(chalk4.green(` New files (${added.length}):`));
|
|
806
|
+
for (const change of added.slice(0, 10)) {
|
|
807
|
+
console.log(chalk4.green(` + ${change.relativePath}`));
|
|
808
|
+
}
|
|
809
|
+
if (added.length > 10) {
|
|
810
|
+
console.log(chalk4.gray(` ... and ${added.length - 10} more files`));
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (modified.length > 0) {
|
|
814
|
+
console.log(chalk4.yellow(` Modified files (${modified.length}):`));
|
|
815
|
+
for (const change of modified.slice(0, 10)) {
|
|
816
|
+
console.log(chalk4.yellow(` ~ ${change.relativePath}`));
|
|
817
|
+
}
|
|
818
|
+
if (modified.length > 10) {
|
|
819
|
+
console.log(chalk4.gray(` ... and ${modified.length - 10} more files`));
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
if (dependencyChanges && hasChanges(dependencyChanges)) {
|
|
824
|
+
console.log(chalk4.cyan.bold("\nPackage.json changes:"));
|
|
825
|
+
displayDependencyChanges(dependencyChanges);
|
|
826
|
+
}
|
|
559
827
|
console.log("");
|
|
828
|
+
if (options.preview) {
|
|
829
|
+
const { showDiff } = await inquirer3.prompt([
|
|
830
|
+
{
|
|
831
|
+
type: "confirm",
|
|
832
|
+
name: "showDiff",
|
|
833
|
+
message: "Show detailed file differences?",
|
|
834
|
+
default: false
|
|
835
|
+
}
|
|
836
|
+
]);
|
|
837
|
+
if (showDiff) {
|
|
838
|
+
for (const change of modifiedFiles) {
|
|
839
|
+
console.log(chalk4.bold(`
|
|
840
|
+
${change.relativePath}:`));
|
|
841
|
+
showFileDiff(change.srcPath, change.destPath);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
560
845
|
if (options.dryRun) {
|
|
561
|
-
log("Dry run mode - no changes
|
|
846
|
+
console.log(chalk4.blue("\ni Dry run mode - no changes were made.\n"));
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
const { selectedCategories } = await inquirer3.prompt([
|
|
850
|
+
{
|
|
851
|
+
type: "checkbox",
|
|
852
|
+
name: "selectedCategories",
|
|
853
|
+
message: "Select which changes to apply:",
|
|
854
|
+
choices: [
|
|
855
|
+
...categories.map((cat) => {
|
|
856
|
+
const count = allFileChanges.filter((c) => c.category === cat).length;
|
|
857
|
+
const categoryNames = {
|
|
858
|
+
config: "Configuration files",
|
|
859
|
+
workflows: "GitHub workflows",
|
|
860
|
+
services: "Docker services",
|
|
861
|
+
scripts: "Scripts",
|
|
862
|
+
packages: "Shared packages"
|
|
863
|
+
};
|
|
864
|
+
return {
|
|
865
|
+
name: `${categoryNames[cat] || cat} (${count} files)`,
|
|
866
|
+
value: cat,
|
|
867
|
+
checked: true
|
|
868
|
+
};
|
|
869
|
+
}),
|
|
870
|
+
...dependencyChanges && hasChanges(dependencyChanges) ? [
|
|
871
|
+
{
|
|
872
|
+
name: "Package.json dependencies",
|
|
873
|
+
value: "dependencies",
|
|
874
|
+
checked: true
|
|
875
|
+
}
|
|
876
|
+
] : []
|
|
877
|
+
]
|
|
878
|
+
}
|
|
879
|
+
]);
|
|
880
|
+
if (selectedCategories.length === 0) {
|
|
881
|
+
log("No changes selected. Update cancelled.", "warn");
|
|
562
882
|
return;
|
|
563
883
|
}
|
|
564
884
|
const { confirm } = await inquirer3.prompt([
|
|
565
885
|
{
|
|
566
886
|
type: "confirm",
|
|
567
887
|
name: "confirm",
|
|
568
|
-
message:
|
|
888
|
+
message: `Apply ${selectedCategories.length} category(ies) of changes?`,
|
|
569
889
|
default: true
|
|
570
890
|
}
|
|
571
891
|
]);
|
|
@@ -573,112 +893,60 @@ async function update(options) {
|
|
|
573
893
|
log("Update cancelled.", "warn");
|
|
574
894
|
return;
|
|
575
895
|
}
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
const spinner = ora3("Updating shared packages...").start();
|
|
595
|
-
const existingPackages = fs4.existsSync(path4.join(projectDir, "packages")) ? fs4.readdirSync(path4.join(projectDir, "packages")).filter((f) => fs4.statSync(path4.join(projectDir, "packages", f)).isDirectory()) : [];
|
|
596
|
-
const { selectedPackages } = await inquirer3.prompt([
|
|
597
|
-
{
|
|
598
|
-
type: "checkbox",
|
|
599
|
-
name: "selectedPackages",
|
|
600
|
-
message: "Select packages to update:",
|
|
601
|
-
choices: SHARED_PACKAGES.map((pkg) => ({
|
|
602
|
-
name: pkg + (existingPackages.includes(pkg) ? " (update)" : " (add new)"),
|
|
603
|
-
value: pkg,
|
|
604
|
-
checked: existingPackages.includes(pkg)
|
|
605
|
-
}))
|
|
896
|
+
const applySpinner = ora3("Applying changes...").start();
|
|
897
|
+
let appliedFiles = 0;
|
|
898
|
+
for (const change of allFileChanges) {
|
|
899
|
+
if (!selectedCategories.includes(change.category)) continue;
|
|
900
|
+
fs4.ensureDirSync(path4.dirname(change.destPath));
|
|
901
|
+
fs4.copySync(change.srcPath, change.destPath, { overwrite: true });
|
|
902
|
+
appliedFiles++;
|
|
903
|
+
}
|
|
904
|
+
if (selectedCategories.includes("dependencies") && dependencyChanges) {
|
|
905
|
+
const templatePkgPath = path4.join(templateDir, "package.json");
|
|
906
|
+
const projectPkgPath = path4.join(projectDir, "package.json");
|
|
907
|
+
const templatePkg = fs4.readJsonSync(templatePkgPath);
|
|
908
|
+
const projectPkg = fs4.readJsonSync(projectPkgPath);
|
|
909
|
+
if (templatePkg.devDependencies) {
|
|
910
|
+
projectPkg.devDependencies = {
|
|
911
|
+
...projectPkg.devDependencies,
|
|
912
|
+
...templatePkg.devDependencies
|
|
913
|
+
};
|
|
606
914
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
}
|
|
618
|
-
fs4.copySync(srcDir, destDir, { overwrite: true });
|
|
619
|
-
if (existingPkgJson) {
|
|
620
|
-
const newPkgJson = fs4.readJsonSync(pkgJsonPath);
|
|
621
|
-
newPkgJson.version = existingPkgJson.version;
|
|
622
|
-
fs4.writeJsonSync(pkgJsonPath, newPkgJson, { spaces: 2 });
|
|
623
|
-
}
|
|
915
|
+
if (templatePkg.dependencies) {
|
|
916
|
+
projectPkg.dependencies = {
|
|
917
|
+
...projectPkg.dependencies,
|
|
918
|
+
...templatePkg.dependencies
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
if (templatePkg.scripts) {
|
|
922
|
+
projectPkg.scripts = {
|
|
923
|
+
...templatePkg.scripts,
|
|
924
|
+
...projectPkg.scripts
|
|
925
|
+
};
|
|
624
926
|
}
|
|
927
|
+
if (projectPkg.dependencies) {
|
|
928
|
+
projectPkg.dependencies = sortObject(projectPkg.dependencies);
|
|
929
|
+
}
|
|
930
|
+
if (projectPkg.devDependencies) {
|
|
931
|
+
projectPkg.devDependencies = sortObject(projectPkg.devDependencies);
|
|
932
|
+
}
|
|
933
|
+
fs4.writeJsonSync(projectPkgPath, projectPkg, { spaces: 2 });
|
|
625
934
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
for (const file of configFiles) {
|
|
633
|
-
const srcFile = path4.join(templateDir, file);
|
|
634
|
-
const destFile = path4.join(projectDir, file);
|
|
635
|
-
if (fs4.existsSync(srcFile)) {
|
|
636
|
-
fs4.copySync(srcFile, destFile, { overwrite: true });
|
|
637
|
-
updated++;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
const huskySrc = path4.join(templateDir, ".husky");
|
|
641
|
-
const huskyDest = path4.join(projectDir, ".husky");
|
|
642
|
-
if (fs4.existsSync(huskySrc)) {
|
|
643
|
-
fs4.copySync(huskySrc, huskyDest, { overwrite: true });
|
|
644
|
-
}
|
|
645
|
-
const vscodeSrc = path4.join(templateDir, ".vscode");
|
|
646
|
-
const vscodeDest = path4.join(projectDir, ".vscode");
|
|
647
|
-
if (fs4.existsSync(vscodeSrc)) {
|
|
648
|
-
fs4.copySync(vscodeSrc, vscodeDest, { overwrite: true });
|
|
649
|
-
}
|
|
650
|
-
spinner.succeed(`Updated ${updated} configuration files`);
|
|
651
|
-
}
|
|
652
|
-
async function updateWorkflows(projectDir, templateDir) {
|
|
653
|
-
const spinner = ora3("Updating GitHub workflows...").start();
|
|
654
|
-
const workflowsSrc = path4.join(templateDir, ".github", "workflows");
|
|
655
|
-
const workflowsDest = path4.join(projectDir, ".github", "workflows");
|
|
656
|
-
if (fs4.existsSync(workflowsSrc)) {
|
|
657
|
-
fs4.ensureDirSync(workflowsDest);
|
|
658
|
-
fs4.copySync(workflowsSrc, workflowsDest, { overwrite: true });
|
|
659
|
-
}
|
|
660
|
-
const actionsSrc = path4.join(templateDir, ".github", "actions");
|
|
661
|
-
const actionsDest = path4.join(projectDir, ".github", "actions");
|
|
662
|
-
if (fs4.existsSync(actionsSrc)) {
|
|
663
|
-
fs4.ensureDirSync(actionsDest);
|
|
664
|
-
fs4.copySync(actionsSrc, actionsDest, { overwrite: true });
|
|
665
|
-
}
|
|
666
|
-
const dependabotSrc = path4.join(templateDir, ".github", "dependabot.yml");
|
|
667
|
-
const dependabotDest = path4.join(projectDir, ".github", "dependabot.yml");
|
|
668
|
-
if (fs4.existsSync(dependabotSrc)) {
|
|
669
|
-
fs4.copySync(dependabotSrc, dependabotDest, { overwrite: true });
|
|
670
|
-
}
|
|
671
|
-
spinner.succeed("Updated GitHub workflows and actions");
|
|
935
|
+
applySpinner.succeed(`Applied ${appliedFiles} file changes`);
|
|
936
|
+
console.log("\n" + chalk4.green.bold("\u2728 Update complete!\n"));
|
|
937
|
+
if (selectedCategories.includes("dependencies")) {
|
|
938
|
+
console.log("Run your package manager install command to install any new dependencies.");
|
|
939
|
+
}
|
|
940
|
+
console.log("");
|
|
672
941
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
spinner.succeed("Updated Docker services");
|
|
942
|
+
function sortObject(obj) {
|
|
943
|
+
return Object.keys(obj).sort().reduce(
|
|
944
|
+
(acc, key) => {
|
|
945
|
+
acc[key] = obj[key];
|
|
946
|
+
return acc;
|
|
947
|
+
},
|
|
948
|
+
{}
|
|
949
|
+
);
|
|
682
950
|
}
|
|
683
951
|
|
|
684
952
|
// src/index.ts
|
|
@@ -691,6 +959,6 @@ program.argument("[project-name]", "Name of the project to create").option("-t,
|
|
|
691
959
|
return init(projectName, options);
|
|
692
960
|
}
|
|
693
961
|
});
|
|
694
|
-
program.command("update").description("Update an existing Nexu project with latest features").option("-p, --packages", "Update only shared packages").option("-c, --config", "Update only configuration files").option("-w, --workflows", "Update only GitHub workflows").option("-s, --services", "Update only Docker services").option("--all", "Update everything (default)").option("--dry-run", "Show what would be updated without making changes").action(update);
|
|
962
|
+
program.command("update").description("Update an existing Nexu project with latest features").option("-p, --packages", "Update only shared packages").option("-c, --config", "Update only configuration files").option("-w, --workflows", "Update only GitHub workflows").option("-s, --services", "Update only Docker services").option("--scripts", "Update only scripts").option("-d, --dependencies", "Update only package.json dependencies").option("--all", "Update everything (default)").option("--dry-run", "Show what would be updated without making changes").option("--preview", "Show detailed file differences before applying").action(update);
|
|
695
963
|
program.command("add <component>").description("Add a component to your project (package, service)").option("-n, --name <name>", "Name for the component").action(add);
|
|
696
964
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nexu",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "CLI to create and update Nexu monorepo projects",
|
|
5
5
|
"author": "Nexu Team",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,18 +23,20 @@
|
|
|
23
23
|
"typecheck": "tsc --noEmit"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
+
"chalk": "^5.3.0",
|
|
26
27
|
"commander": "^12.0.0",
|
|
28
|
+
"diff": "^8.0.3",
|
|
27
29
|
"fs-extra": "^11.2.0",
|
|
28
30
|
"inquirer": "^9.2.0",
|
|
29
|
-
"chalk": "^5.3.0",
|
|
30
31
|
"ora": "^8.0.0",
|
|
31
32
|
"semver": "^7.6.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
35
|
+
"@types/diff": "^8.0.0",
|
|
34
36
|
"@types/fs-extra": "^11.0.0",
|
|
35
37
|
"@types/inquirer": "^9.0.0",
|
|
36
|
-
"@types/semver": "^7.5.0",
|
|
37
38
|
"@types/node": "^20.0.0",
|
|
39
|
+
"@types/semver": "^7.5.0",
|
|
38
40
|
"tsup": "^8.0.0",
|
|
39
41
|
"typescript": "^5.4.0"
|
|
40
42
|
}
|