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.
Files changed (2) hide show
  1. package/dist/index.js +392 -124
  2. 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 updateAll = options.all || !options.packages && !options.config && !options.workflows && !options.services;
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
- console.log("Components to update:");
555
- if (updateTargets.packages) console.log(chalk4.cyan(" - Shared packages"));
556
- if (updateTargets.config) console.log(chalk4.cyan(" - Configuration files"));
557
- if (updateTargets.workflows) console.log(chalk4.cyan(" - GitHub workflows"));
558
- if (updateTargets.services) console.log(chalk4.cyan(" - Docker services"));
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 will be made", "info");
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: "Proceed with update?",
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 templateDir = getTemplateDir3();
577
- if (updateTargets.packages) {
578
- await updatePackages(projectDir, templateDir);
579
- }
580
- if (updateTargets.config) {
581
- await updateConfig(projectDir, templateDir);
582
- }
583
- if (updateTargets.workflows) {
584
- await updateWorkflows(projectDir, templateDir);
585
- }
586
- if (updateTargets.services) {
587
- await updateServices(projectDir, templateDir);
588
- }
589
- console.log("\n" + chalk4.green.bold("\u2728 Update complete!\n"));
590
- console.log("Run " + chalk4.cyan("pnpm install") + " to install any new dependencies.");
591
- console.log("");
592
- }
593
- async function updatePackages(projectDir, templateDir) {
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
- spinner.start("Updating packages...");
609
- for (const pkg of selectedPackages) {
610
- const srcDir = path4.join(templateDir, "packages", pkg);
611
- const destDir = path4.join(projectDir, "packages", pkg);
612
- if (fs4.existsSync(srcDir)) {
613
- let existingPkgJson = null;
614
- const pkgJsonPath = path4.join(destDir, "package.json");
615
- if (fs4.existsSync(pkgJsonPath)) {
616
- existingPkgJson = fs4.readJsonSync(pkgJsonPath);
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
- spinner.succeed(`Updated ${selectedPackages.length} packages`);
627
- }
628
- async function updateConfig(projectDir, templateDir) {
629
- const spinner = ora3("Updating configuration files...").start();
630
- const configFiles = TEMPLATE_DIRS.config;
631
- let updated = 0;
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
- async function updateServices(projectDir, templateDir) {
674
- const spinner = ora3("Updating Docker services...").start();
675
- const servicesSrc = path4.join(templateDir, "services");
676
- const servicesDest = path4.join(projectDir, "services");
677
- if (fs4.existsSync(servicesSrc)) {
678
- fs4.ensureDirSync(servicesDest);
679
- copyWithMerge(servicesSrc, servicesDest, true);
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.0",
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
  }