create-velocity-astro 1.5.3 → 1.6.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/dist/index.js CHANGED
@@ -3,13 +3,13 @@
3
3
  // src/cli.ts
4
4
  import mri from "mri";
5
5
  import { resolve as resolve2 } from "path";
6
- import { existsSync as existsSync5 } from "fs";
7
- import * as p3 from "@clack/prompts";
6
+ import { existsSync as existsSync8 } from "fs";
7
+ import * as p5 from "@clack/prompts";
8
8
  import pc4 from "picocolors";
9
9
 
10
10
  // src/prompts.ts
11
11
  import * as p from "@clack/prompts";
12
- import pc2 from "picocolors";
12
+ import pc from "picocolors";
13
13
 
14
14
  // src/utils/validate.ts
15
15
  function validateProjectName(name) {
@@ -163,38 +163,6 @@ function getSelectionStats(resolved, registry) {
163
163
  };
164
164
  }
165
165
 
166
- // src/display.ts
167
- import gradient from "gradient-string";
168
- import figures from "figures";
169
- import pc from "picocolors";
170
- var velocityGrad = gradient(["#FF6B35", "#F94C10", "#E63900"]);
171
- var successGrad = gradient(["#22c55e", "#10b981"]);
172
- var BANNER = `
173
- \u2588 \u2588 \u2588\u2580\u2580 \u2588 \u2588\u2580\u2588 \u2588\u2580\u2580 \u2588 \u2580\u2588\u2580 \u2588 \u2588
174
- \u2580\u2584\u2580 \u2588\u2588\u2584 \u2588\u2584\u2584 \u2588\u2584\u2588 \u2588\u2584\u2584 \u2588 \u2588 \u2580\u2584\u2580
175
- `;
176
- function showBanner() {
177
- console.log();
178
- console.log(velocityGrad.multiline(BANNER));
179
- console.log(pc.dim(" Astro 6 & Tailwind v4 Starter Kit"));
180
- console.log();
181
- }
182
- var symbols = {
183
- rocket: "\u{1F680}",
184
- sparkles: "\u2728",
185
- package: "\u{1F4E6}",
186
- check: figures.tick,
187
- pointer: figures.pointer
188
- };
189
- function showSuccessBanner() {
190
- console.log();
191
- console.log(successGrad(" \u2728 Project created successfully! \u2728"));
192
- console.log();
193
- }
194
- function velocityGradient(text2) {
195
- return velocityGrad(text2);
196
- }
197
-
198
166
  // src/prompts.ts
199
167
  function parsePageNames(input) {
200
168
  if (!input.trim()) return [];
@@ -207,7 +175,7 @@ async function promptComponentMode() {
207
175
  {
208
176
  value: "all",
209
177
  label: "All",
210
- hint: "Include all 26 optional components"
178
+ hint: "Include all 24 optional components"
211
179
  },
212
180
  {
213
181
  value: "categories",
@@ -323,7 +291,7 @@ async function getComponentSelection(defaultSelection) {
323
291
  const resolved = resolveDependencies(selection, registry);
324
292
  const stats = getSelectionStats(resolved, registry);
325
293
  p.log.info(
326
- pc2.dim(`Selected ${stats.componentCount} components (${resolved.files.length} files)`)
294
+ pc.dim(`Selected ${stats.componentCount} components (${resolved.files.length} files)`)
327
295
  );
328
296
  return selection;
329
297
  }
@@ -334,13 +302,13 @@ async function getComponentSelection(defaultSelection) {
334
302
  const stats = getSelectionStats(resolved, registry);
335
303
  if (resolved.components.length > components.length) {
336
304
  p.log.info(
337
- pc2.dim(
305
+ pc.dim(
338
306
  `Selected ${components.length} components + ${resolved.components.length - components.length} dependencies (${resolved.files.length} files)`
339
307
  )
340
308
  );
341
309
  } else {
342
310
  p.log.info(
343
- pc2.dim(`Selected ${stats.componentCount} components (${resolved.files.length} files)`)
311
+ pc.dim(`Selected ${stats.componentCount} components (${resolved.files.length} files)`)
344
312
  );
345
313
  }
346
314
  return selection;
@@ -481,37 +449,35 @@ async function runPrompts(defaults = {}) {
481
449
  };
482
450
  }
483
451
  function showIntro() {
484
- showBanner();
452
+ console.log();
453
+ p.intro(pc.bgCyan(pc.black(" Create Velocity ")));
485
454
  }
486
455
  function showOutro(projectName, packageManager) {
487
456
  const runCmd = packageManager === "npm" ? "npm run" : packageManager;
488
- showSuccessBanner();
489
- console.log(pc2.bold(" Next steps:"));
490
- console.log();
491
- console.log(` ${pc2.dim("$")} ${pc2.cyan(`cd ${projectName}`)}`);
492
- console.log(` ${pc2.dim("$")} ${pc2.cyan(`${runCmd} dev`)}`);
493
- console.log();
494
- console.log(` ${pc2.dim("Docs:")} ${pc2.cyan("https://docs.deployvelocity.com")}`);
495
- console.log();
496
- console.log(velocityGradient(" Happy building! \u{1F680}"));
497
- console.log();
457
+ p.note(
458
+ [
459
+ `cd ${projectName}`,
460
+ `${runCmd} dev`
461
+ ].join("\n"),
462
+ "Next steps"
463
+ );
464
+ p.outro(pc.green("Happy building!"));
498
465
  }
499
466
  function showError(message) {
500
- p.log.error(pc2.red(message));
467
+ p.log.error(pc.red(message));
501
468
  }
502
469
  function showWarning(message) {
503
- p.log.warn(pc2.yellow(message));
470
+ p.log.warn(pc.yellow(message));
504
471
  }
505
472
  function showSuccess(message) {
506
- p.log.success(pc2.green(message));
473
+ p.log.success(pc.green(message));
507
474
  }
508
475
 
509
476
  // src/scaffold.ts
510
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, copyFileSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2, rmSync } from "fs";
511
- import { join as join2, dirname as dirname2 } from "path";
477
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readdirSync as readdirSync2, copyFileSync as copyFileSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3, rmSync } from "fs";
478
+ import { join as join4, dirname as dirname3 } from "path";
512
479
  import * as p2 from "@clack/prompts";
513
480
  import { execa as execa2 } from "execa";
514
- import pc3 from "picocolors";
515
481
  import { downloadTemplate } from "giget";
516
482
 
517
483
  // src/template.ts
@@ -627,23 +593,18 @@ function addI18nRouteEntry(targetDir, pageName) {
627
593
  }
628
594
  function addI18nTranslationKeys(targetDir, routeId, title) {
629
595
  const locales = ["en", "es", "fr"];
630
- const defaultLocale = "en";
631
596
  for (const locale of locales) {
632
597
  const translationPath = join(targetDir, "src", "i18n", "translations", `${locale}.ts`);
633
598
  if (!existsSync2(translationPath)) {
634
599
  continue;
635
600
  }
636
601
  let content = readFileSync(translationPath, "utf-8");
637
- const isDefaultLocale = locale === defaultLocale;
638
- const navLabel = isDefaultLocale ? title : `[TODO: Translate] ${title}`;
639
- const pageTitle = isDefaultLocale ? title : `[TODO: Translate] ${title}`;
640
- const pageDesc = isDefaultLocale ? `Add your ${title.toLowerCase()} page description here.` : `[TODO: Translate] Add your ${title.toLowerCase()} page description here.`;
641
602
  if (!content.includes(`${routeId}:`)) {
642
603
  const navSectionMatch = content.match(/nav:\s*\{([^}]+)\}/);
643
604
  if (navSectionMatch) {
644
605
  const navSection = navSectionMatch[0];
645
606
  const insertPoint = navSection.lastIndexOf("}");
646
- const newNavSection = navSection.slice(0, insertPoint) + ` ${routeId}: '${navLabel}',
607
+ const newNavSection = navSection.slice(0, insertPoint) + ` ${routeId}: '${title}',
647
608
  ` + navSection.slice(insertPoint);
648
609
  content = content.replace(navSection, newNavSection);
649
610
  }
@@ -655,8 +616,8 @@ function addI18nTranslationKeys(targetDir, routeId, title) {
655
616
  const pageTranslations = `
656
617
  // ${title} page
657
618
  ${routeId}: {
658
- title: '${pageTitle}',
659
- description: '${pageDesc}',
619
+ title: '${title}',
620
+ description: 'Add your ${title.toLowerCase()} page description here.',
660
621
  },
661
622
 
662
623
  `;
@@ -696,47 +657,6 @@ import ${layoutName} from '@/layouts/${layoutName}.astro';
696
657
  </${layoutName}>
697
658
  `;
698
659
  }
699
- function generateI18nDefaultLocaleTemplate(pageName, layout) {
700
- const title = toTitle(pageName);
701
- const layoutName = layout === "landing" ? "LandingLayout" : "PageLayout";
702
- const routeId = toRouteId(pageName);
703
- const titleKey = `${routeId}.title`;
704
- const descKey = `${routeId}.description`;
705
- return `---
706
- import ${layoutName} from '@/layouts/${layoutName}.astro';
707
- import { defaultLocale } from '@/i18n/config';
708
- import { useTranslations } from '@/i18n/index';
709
-
710
- const t = useTranslations(defaultLocale);
711
- ---
712
-
713
- <${layoutName}
714
- title={t('${titleKey}') || '${title}'}
715
- description={t('${descKey}') || 'Add your description here'}
716
- lang={defaultLocale}
717
- routeId="${routeId}"
718
- >
719
- <!-- Hero Section -->
720
- <section class="py-20 bg-secondary">
721
- <div class="container">
722
- <h1 class="text-4xl font-bold text-foreground">
723
- {t('${titleKey}') || '${title}'}
724
- </h1>
725
- <p class="mt-4 text-foreground-muted max-w-2xl">
726
- {t('${descKey}') || 'Add your content here.'}
727
- </p>
728
- </div>
729
- </section>
730
-
731
- <!-- Content Section -->
732
- <section class="py-16">
733
- <div class="container">
734
- <!-- Your content -->
735
- </div>
736
- </section>
737
- </${layoutName}>
738
- `;
739
- }
740
660
  function generateI18nPageTemplate(pageName, layout) {
741
661
  const title = toTitle(pageName);
742
662
  const layoutName = layout === "landing" ? "LandingLayout" : "PageLayout";
@@ -808,7 +728,7 @@ async function generatePages(targetDir, pages, layout, isI18n) {
808
728
  }
809
729
  for (const pageName of pages) {
810
730
  const filePath = join(pagesDir, `${pageName}.astro`);
811
- const template = isI18n ? generateI18nDefaultLocaleTemplate(pageName, layout) : generatePageTemplate(pageName, layout);
731
+ const template = generatePageTemplate(pageName, layout);
812
732
  writeFileSync(filePath, template);
813
733
  generatedFiles.push(`src/pages/${pageName}.astro`);
814
734
  if (!isI18n) {
@@ -832,6 +752,68 @@ async function generatePages(targetDir, pages, layout, isI18n) {
832
752
  return generatedFiles;
833
753
  }
834
754
 
755
+ // src/utils/velocity-config.ts
756
+ import { existsSync as existsSync4 } from "fs";
757
+ import { join as join3 } from "path";
758
+
759
+ // src/utils/fs.ts
760
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, statSync, copyFileSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
761
+ import { join as join2, dirname as dirname2 } from "path";
762
+ function isEmptyDir(path) {
763
+ if (!existsSync3(path)) return true;
764
+ const files = readdirSync(path);
765
+ return files.length === 0 || files.length === 1 && files[0] === ".git";
766
+ }
767
+ function readJson(path) {
768
+ const content = readFileSync2(path, "utf-8");
769
+ return JSON.parse(content);
770
+ }
771
+ function writeJson(path, data) {
772
+ writeFileSync2(path, JSON.stringify(data, null, 2) + "\n");
773
+ }
774
+
775
+ // src/utils/velocity-config.ts
776
+ var CONFIG_FILENAME = ".velocity.json";
777
+ function readVelocityConfig(projectDir) {
778
+ const configPath = join3(projectDir, CONFIG_FILENAME);
779
+ if (!existsSync4(configPath)) {
780
+ return null;
781
+ }
782
+ return readJson(configPath);
783
+ }
784
+ function writeVelocityConfig(projectDir, config) {
785
+ const configPath = join3(projectDir, CONFIG_FILENAME);
786
+ writeJson(configPath, config);
787
+ }
788
+ function createInitialConfig(options, version) {
789
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
790
+ let componentsValue;
791
+ switch (options.componentSelection.mode) {
792
+ case "all":
793
+ componentsValue = "all";
794
+ break;
795
+ case "none":
796
+ componentsValue = "none";
797
+ break;
798
+ case "categories":
799
+ componentsValue = `categories:${options.componentSelection.categories?.join(",") ?? ""}`;
800
+ break;
801
+ case "individual":
802
+ componentsValue = `individual:${options.componentSelection.components?.join(",") ?? ""}`;
803
+ break;
804
+ }
805
+ return {
806
+ version,
807
+ createdAt: today,
808
+ updatedAt: today,
809
+ features: {
810
+ demo: options.demo,
811
+ i18n: options.i18n,
812
+ components: componentsValue
813
+ }
814
+ };
815
+ }
816
+
835
817
  // src/scaffold.ts
836
818
  var TEMPLATE_REPO = "github:southwellmedia/velocity";
837
819
  var CLEANUP_ITEMS = [
@@ -842,8 +824,9 @@ var CLEANUP_ITEMS = [
842
824
  ".git"
843
825
  ];
844
826
  var DEMO_CONTENT = [
845
- // Landing page components (NOT hero - that's a core component)
827
+ // Landing page components
846
828
  "src/components/landing",
829
+ "src/components/hero",
847
830
  // Landing-specific pages (base paths)
848
831
  "src/pages/index.astro",
849
832
  "src/pages/about.astro",
@@ -868,24 +851,24 @@ var OPTIONAL_COMPONENT_DIRS = [
868
851
  "src/components/hero"
869
852
  ];
870
853
  function copyTemplateFiles(src, dest) {
871
- if (!existsSync3(dest)) {
872
- mkdirSync2(dest, { recursive: true });
854
+ if (!existsSync5(dest)) {
855
+ mkdirSync3(dest, { recursive: true });
873
856
  }
874
- const entries = readdirSync(src, { withFileTypes: true });
857
+ const entries = readdirSync2(src, { withFileTypes: true });
875
858
  for (const entry of entries) {
876
- const srcPath = join2(src, entry.name);
877
- const destPath = join2(dest, entry.name);
859
+ const srcPath = join4(src, entry.name);
860
+ const destPath = join4(dest, entry.name);
878
861
  if (entry.isDirectory()) {
879
862
  copyTemplateFiles(srcPath, destPath);
880
863
  } else {
881
- copyFileSync(srcPath, destPath);
864
+ copyFileSync2(srcPath, destPath);
882
865
  }
883
866
  }
884
867
  }
885
868
  function removeItems(targetDir, items) {
886
869
  for (const item of items) {
887
- const itemPath = join2(targetDir, item);
888
- if (existsSync3(itemPath)) {
870
+ const itemPath = join4(targetDir, item);
871
+ if (existsSync5(itemPath)) {
889
872
  try {
890
873
  rmSync(itemPath, { recursive: true, force: true });
891
874
  } catch {
@@ -900,22 +883,22 @@ function keepOnlyFiles(targetDir, filesToKeep) {
900
883
  "src/components/hero"
901
884
  ];
902
885
  for (const dir of componentDirs) {
903
- const dirPath = join2(targetDir, dir);
904
- if (!existsSync3(dirPath)) continue;
905
- const entries = readdirSync(dirPath, { withFileTypes: true });
886
+ const dirPath = join4(targetDir, dir);
887
+ if (!existsSync5(dirPath)) continue;
888
+ const entries = readdirSync2(dirPath, { withFileTypes: true });
906
889
  for (const entry of entries) {
907
890
  if (entry.isFile()) {
908
891
  const relativePath = `${dir}/${entry.name}`;
909
892
  if (!filesToKeep.has(relativePath)) {
910
893
  try {
911
- rmSync(join2(dirPath, entry.name), { force: true });
894
+ rmSync(join4(dirPath, entry.name), { force: true });
912
895
  } catch {
913
896
  }
914
897
  }
915
898
  }
916
899
  }
917
900
  try {
918
- const remaining = readdirSync(dirPath);
901
+ const remaining = readdirSync2(dirPath);
919
902
  if (remaining.length === 0) {
920
903
  rmSync(dirPath, { recursive: true, force: true });
921
904
  }
@@ -924,21 +907,21 @@ function keepOnlyFiles(targetDir, filesToKeep) {
924
907
  }
925
908
  }
926
909
  function updatePackageJson(targetDir, projectName) {
927
- const pkgPath = join2(targetDir, "package.json");
928
- if (!existsSync3(pkgPath)) {
910
+ const pkgPath = join4(targetDir, "package.json");
911
+ if (!existsSync5(pkgPath)) {
929
912
  throw new Error("package.json not found in template");
930
913
  }
931
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
914
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
932
915
  pkg.name = projectName;
933
916
  pkg.version = "0.1.0";
934
917
  delete pkg.repository;
935
918
  delete pkg.bugs;
936
919
  delete pkg.homepage;
937
- writeFileSync2(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
920
+ writeFileSync3(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
938
921
  }
939
922
  function applyBaseTemplate(targetDir) {
940
923
  const baseTemplate = getBaseTemplatePath();
941
- if (existsSync3(baseTemplate)) {
924
+ if (existsSync5(baseTemplate)) {
942
925
  copyTemplateFiles(baseTemplate, targetDir);
943
926
  }
944
927
  }
@@ -946,25 +929,15 @@ function applyI18nOverlay(targetDir) {
946
929
  const i18nTemplate = getI18nTemplatePath();
947
930
  copyTemplateFiles(i18nTemplate, targetDir);
948
931
  }
949
- function removeComponentsRoute(targetDir, isI18n) {
950
- const routesPath = isI18n ? join2(targetDir, "src", "i18n", "routes.ts") : join2(targetDir, "src", "config", "routes.ts");
951
- if (!existsSync3(routesPath)) return;
952
- let content = readFileSync2(routesPath, "utf-8");
953
- content = content.replace(
954
- /\n\s*\/\/ Components showcase\n\s*components:\s*\{[^}]+\},?\n/g,
955
- "\n"
956
- );
957
- writeFileSync2(routesPath, content);
958
- }
959
932
  function createContentDirectories(targetDir) {
960
933
  const contentDirs = [
961
934
  "src/content/blog"
962
935
  ];
963
936
  for (const dir of contentDirs) {
964
- const dirPath = join2(targetDir, dir);
965
- if (!existsSync3(dirPath)) {
966
- mkdirSync2(dirPath, { recursive: true });
967
- writeFileSync2(join2(dirPath, ".gitkeep"), "");
937
+ const dirPath = join4(targetDir, dir);
938
+ if (!existsSync5(dirPath)) {
939
+ mkdirSync3(dirPath, { recursive: true });
940
+ writeFileSync3(join4(dirPath, ".gitkeep"), "");
968
941
  }
969
942
  }
970
943
  }
@@ -987,142 +960,569 @@ async function applyComponentSelection(targetDir, selection) {
987
960
  const filesToKeep = new Set(resolved.files);
988
961
  keepOnlyFiles(targetDir, filesToKeep);
989
962
  if (resolved.utilities.includes("cn")) {
990
- const cnPath = join2(targetDir, "src/lib/cn.ts");
991
- const cnDir = dirname2(cnPath);
992
- if (!existsSync3(cnDir)) {
993
- mkdirSync2(cnDir, { recursive: true });
963
+ const cnPath = join4(targetDir, "src/lib/cn.ts");
964
+ const cnDir = dirname3(cnPath);
965
+ if (!existsSync5(cnDir)) {
966
+ mkdirSync3(cnDir, { recursive: true });
994
967
  }
995
968
  }
996
969
  }
997
970
  async function scaffold(options) {
998
971
  const { projectName, targetDir, demo, componentSelection, i18n, pages, pageLayout, packageManager } = options;
999
- const spinner2 = p2.spinner();
1000
- spinner2.start("Downloading template from GitHub...");
972
+ const spinner3 = p2.spinner();
973
+ spinner3.start("Downloading template from GitHub...");
1001
974
  try {
1002
975
  await downloadTemplate(TEMPLATE_REPO, {
1003
976
  dir: targetDir,
1004
977
  force: true
1005
978
  });
1006
979
  removeItems(targetDir, CLEANUP_ITEMS);
1007
- spinner2.stop("Template downloaded");
980
+ spinner3.stop("Template downloaded");
1008
981
  } catch (error) {
1009
- spinner2.stop("Failed to download template");
982
+ spinner3.stop("Failed to download template");
1010
983
  throw new Error(
1011
984
  `Could not download template from GitHub. Please check your internet connection.
1012
985
  ${error instanceof Error ? error.message : ""}`
1013
986
  );
1014
987
  }
1015
988
  if (componentSelection.mode !== "all") {
1016
- spinner2.start("Configuring components...");
989
+ spinner3.start("Configuring components...");
1017
990
  try {
1018
991
  await applyComponentSelection(targetDir, componentSelection);
1019
- spinner2.stop("Components configured");
992
+ spinner3.stop("Components configured");
1020
993
  } catch (error) {
1021
- spinner2.stop("Failed to configure components");
994
+ spinner3.stop("Failed to configure components");
1022
995
  throw error;
1023
996
  }
1024
997
  }
1025
998
  if (i18n) {
1026
- spinner2.start("Adding i18n support...");
999
+ spinner3.start("Adding i18n support...");
1027
1000
  try {
1028
1001
  applyI18nOverlay(targetDir);
1029
- spinner2.stop("i18n support added");
1002
+ spinner3.stop("i18n support added");
1030
1003
  } catch (error) {
1031
- spinner2.stop("Failed to add i18n support");
1004
+ spinner3.stop("Failed to add i18n support");
1032
1005
  throw error;
1033
1006
  }
1034
1007
  }
1035
1008
  if (!demo) {
1036
- spinner2.start("Configuring minimal template...");
1009
+ spinner3.start("Configuring minimal template...");
1037
1010
  removeItems(targetDir, DEMO_CONTENT);
1038
1011
  applyBaseTemplate(targetDir);
1039
1012
  createContentDirectories(targetDir);
1040
- removeComponentsRoute(targetDir, i18n);
1041
- spinner2.stop("Minimal template configured");
1013
+ spinner3.stop("Minimal template configured");
1042
1014
  }
1043
1015
  if (pages.length > 0) {
1044
- spinner2.start(`Generating ${pages.length} starter page${pages.length > 1 ? "s" : ""}...`);
1016
+ spinner3.start(`Generating ${pages.length} starter page${pages.length > 1 ? "s" : ""}...`);
1045
1017
  try {
1046
1018
  const generatedFiles = await generatePages(targetDir, pages, pageLayout, i18n);
1047
- spinner2.stop(`Generated ${generatedFiles.length} page file${generatedFiles.length > 1 ? "s" : ""}`);
1048
- if (i18n) {
1049
- showWarning(`i18n pages created with English-only content.
1050
-
1051
- You must manually:
1052
- 1. Translate URL slugs in src/i18n/routes.ts
1053
- 2. Add translations to src/i18n/translations/es.ts
1054
- 3. Add translations to src/i18n/translations/fr.ts
1055
-
1056
- Look for [TODO: Translate] markers in the translation files.`);
1057
- }
1019
+ spinner3.stop(`Generated ${generatedFiles.length} page file${generatedFiles.length > 1 ? "s" : ""}`);
1058
1020
  } catch (error) {
1059
- spinner2.stop("Failed to generate pages");
1021
+ spinner3.stop("Failed to generate pages");
1060
1022
  throw error;
1061
1023
  }
1062
1024
  }
1063
- spinner2.start("Configuring project...");
1025
+ spinner3.start("Configuring project...");
1064
1026
  try {
1065
1027
  updatePackageJson(targetDir, projectName);
1066
- spinner2.stop("Project configured");
1028
+ spinner3.stop("Project configured");
1067
1029
  } catch (error) {
1068
- spinner2.stop("Failed to configure project");
1030
+ spinner3.stop("Failed to configure project");
1069
1031
  throw error;
1070
1032
  }
1071
- spinner2.start("Initializing git repository...");
1033
+ try {
1034
+ let templateVersion = "0.1.0-beta";
1035
+ const manifestPath = join4(targetDir, "velocity-manifest.json");
1036
+ const pkgPath = join4(targetDir, "package.json");
1037
+ if (existsSync5(manifestPath)) {
1038
+ const manifest = readJson(manifestPath);
1039
+ if (manifest.version) templateVersion = manifest.version;
1040
+ } else if (existsSync5(pkgPath)) {
1041
+ const pkg = readJson(pkgPath);
1042
+ if (pkg.version) templateVersion = pkg.version;
1043
+ }
1044
+ const velocityConfig = createInitialConfig(options, templateVersion);
1045
+ writeVelocityConfig(targetDir, velocityConfig);
1046
+ } catch {
1047
+ }
1048
+ spinner3.start("Initializing git repository...");
1072
1049
  const gitInitialized = await initGit(targetDir);
1073
1050
  if (gitInitialized) {
1074
- spinner2.stop("Git repository initialized");
1051
+ spinner3.stop("Git repository initialized");
1075
1052
  } else {
1076
- spinner2.stop("Git not available, skipping");
1053
+ spinner3.stop("Git not available, skipping");
1077
1054
  }
1078
- console.log();
1079
- p2.log.step(`Installing dependencies with ${packageManager}...`);
1080
- console.log(pc3.dim("\u2500".repeat(50)));
1055
+ spinner3.start(`Installing dependencies with ${packageManager}...`);
1081
1056
  try {
1082
1057
  const installCmd = getInstallCommand(packageManager);
1083
1058
  const [cmd, ...args] = installCmd.split(" ");
1084
- await execa2(cmd, args, {
1059
+ await execa2(cmd, args, { cwd: targetDir });
1060
+ spinner3.stop("Dependencies installed");
1061
+ } catch {
1062
+ spinner3.stop("Failed to install dependencies");
1063
+ showWarning(`Run "${getInstallCommand(packageManager)}" manually to install dependencies`);
1064
+ }
1065
+ showSuccess(`Project "${projectName}" created successfully!`);
1066
+ }
1067
+
1068
+ // src/upgrade.ts
1069
+ import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, copyFileSync as copyFileSync3, readdirSync as readdirSync4, rmSync as rmSync2, statSync as statSync3 } from "fs";
1070
+ import { execSync } from "child_process";
1071
+ import { join as join6, dirname as dirname4, relative as relative2 } from "path";
1072
+ import { tmpdir } from "os";
1073
+ import * as p4 from "@clack/prompts";
1074
+ import pc3 from "picocolors";
1075
+ import { downloadTemplate as downloadTemplate2 } from "giget";
1076
+
1077
+ // src/utils/diff.ts
1078
+ import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
1079
+ import { join as join5, relative } from "path";
1080
+ function walkDir(dir, baseDir) {
1081
+ const results = [];
1082
+ if (!existsSync6(dir)) return results;
1083
+ const entries = readdirSync3(dir, { withFileTypes: true });
1084
+ for (const entry of entries) {
1085
+ const fullPath = join5(dir, entry.name);
1086
+ if (entry.isDirectory()) {
1087
+ results.push(...walkDir(fullPath, baseDir));
1088
+ } else {
1089
+ results.push(relative(baseDir, fullPath));
1090
+ }
1091
+ }
1092
+ return results;
1093
+ }
1094
+ function expandPaths(paths, freshDir) {
1095
+ const files = [];
1096
+ for (const p6 of paths) {
1097
+ const fullPath = join5(freshDir, p6);
1098
+ if (p6.endsWith("/") || existsSync6(fullPath) && statSync2(fullPath).isDirectory()) {
1099
+ files.push(...walkDir(fullPath, freshDir));
1100
+ } else {
1101
+ files.push(p6);
1102
+ }
1103
+ }
1104
+ return [...new Set(files)];
1105
+ }
1106
+ function diffProjects(currentDir, freshDir, manifest) {
1107
+ const diffs = [];
1108
+ const safeFiles = expandPaths(manifest.files.safe, freshDir);
1109
+ for (const filePath of safeFiles) {
1110
+ const currentPath = join5(currentDir, filePath);
1111
+ const freshPath = join5(freshDir, filePath);
1112
+ if (!existsSync6(freshPath)) continue;
1113
+ if (!existsSync6(currentPath)) {
1114
+ diffs.push({ path: filePath, status: "added", category: "safe" });
1115
+ } else {
1116
+ const currentContent = readFileSync4(currentPath);
1117
+ const freshContent = readFileSync4(freshPath);
1118
+ if (Buffer.compare(currentContent, freshContent) === 0) {
1119
+ diffs.push({ path: filePath, status: "unchanged", category: "safe" });
1120
+ } else {
1121
+ diffs.push({ path: filePath, status: "modified", category: "safe" });
1122
+ }
1123
+ }
1124
+ }
1125
+ return diffs;
1126
+ }
1127
+ function summarizeDiffs(diffs) {
1128
+ let added = 0;
1129
+ let modified = 0;
1130
+ let unchanged = 0;
1131
+ for (const diff of diffs) {
1132
+ switch (diff.status) {
1133
+ case "added":
1134
+ added++;
1135
+ break;
1136
+ case "modified":
1137
+ modified++;
1138
+ break;
1139
+ case "unchanged":
1140
+ unchanged++;
1141
+ break;
1142
+ }
1143
+ }
1144
+ return { added, modified, unchanged };
1145
+ }
1146
+
1147
+ // src/upgrade-prompts.ts
1148
+ import * as p3 from "@clack/prompts";
1149
+ import pc2 from "picocolors";
1150
+ function showUpgradeIntro(currentVersion, latestVersion) {
1151
+ console.log();
1152
+ p3.intro(pc2.bgCyan(pc2.black(" Velocity Upgrade ")));
1153
+ p3.log.info(
1154
+ `Current version: ${pc2.dim(`v${currentVersion}`)}
1155
+ Latest version: ${pc2.green(`v${latestVersion}`)}`
1156
+ );
1157
+ }
1158
+ function showChangeSummary(diffs, manifest) {
1159
+ const { added, modified } = summarizeDiffs(diffs);
1160
+ const depUpdated = Object.keys(manifest.dependencies.update).length;
1161
+ const depRemoved = manifest.dependencies.remove.length;
1162
+ const depAdded = Object.keys(manifest.dependencies.add).length;
1163
+ const migrationCount = manifest.migrations.length;
1164
+ const lines = [];
1165
+ if (modified > 0) {
1166
+ lines.push(` ${pc2.yellow(`${modified}`)} file${modified !== 1 ? "s" : ""} modified ${pc2.dim("(framework components, layouts, utilities)")}`);
1167
+ }
1168
+ if (added > 0) {
1169
+ lines.push(` ${pc2.green(`${added}`)} file${added !== 1 ? "s" : ""} added ${pc2.dim("(new framework files)")}`);
1170
+ }
1171
+ if (depUpdated > 0) {
1172
+ lines.push(` ${pc2.cyan(`${depUpdated}`)} dependenc${depUpdated !== 1 ? "ies" : "y"} updated`);
1173
+ }
1174
+ if (depRemoved > 0) {
1175
+ lines.push(` ${pc2.red(`${depRemoved}`)} dependenc${depRemoved !== 1 ? "ies" : "y"} removed`);
1176
+ }
1177
+ if (depAdded > 0) {
1178
+ lines.push(` ${pc2.green(`${depAdded}`)} dependenc${depAdded !== 1 ? "ies" : "y"} added`);
1179
+ }
1180
+ if (migrationCount > 0) {
1181
+ lines.push(` ${pc2.yellow(`${migrationCount}`)} manual migration step${migrationCount !== 1 ? "s" : ""}`);
1182
+ }
1183
+ if (lines.length > 0) {
1184
+ p3.log.message(pc2.bold("Changes to apply:") + "\n" + lines.join("\n"));
1185
+ }
1186
+ }
1187
+ async function confirmUpgrade(dryRun) {
1188
+ if (dryRun) {
1189
+ p3.log.info(pc2.dim("Dry run \u2014 no changes will be made."));
1190
+ return false;
1191
+ }
1192
+ const proceed = await p3.confirm({
1193
+ message: "Proceed with upgrade?",
1194
+ initialValue: true
1195
+ });
1196
+ if (p3.isCancel(proceed) || !proceed) {
1197
+ p3.cancel("Upgrade cancelled.");
1198
+ return false;
1199
+ }
1200
+ return true;
1201
+ }
1202
+ function showManualSteps(migrations, matchResults) {
1203
+ if (migrations.length === 0) return;
1204
+ const lines = [];
1205
+ for (let i = 0; i < migrations.length; i++) {
1206
+ const step = migrations[i];
1207
+ const matches = matchResults.get(step.title) ?? [];
1208
+ lines.push(`${pc2.bold(`${i + 1}. ${step.title}`)}`);
1209
+ lines.push(` ${step.description}`);
1210
+ if (matches.length > 0) {
1211
+ lines.push(` ${pc2.yellow("\u26A0")} Found matches in: ${matches.join(", ")}`);
1212
+ }
1213
+ lines.push("");
1214
+ }
1215
+ p3.log.warning(pc2.bold("Manual steps required:") + "\n\n" + lines.join("\n"));
1216
+ }
1217
+ function showUpgradeOutro(hasDepChanges) {
1218
+ if (hasDepChanges) {
1219
+ p3.log.info(`Run ${pc2.cyan("pnpm install")} to update dependencies.`);
1220
+ }
1221
+ p3.outro(pc2.green("Upgrade complete! Review the manual steps above."));
1222
+ }
1223
+ async function warnDirtyGit(yes) {
1224
+ if (yes) {
1225
+ p3.log.warn(pc2.yellow("You have uncommitted changes. Proceeding anyway (--yes)."));
1226
+ return true;
1227
+ }
1228
+ p3.log.warn(pc2.yellow("You have uncommitted changes. We recommend committing or stashing first."));
1229
+ const proceed = await p3.confirm({
1230
+ message: "Continue anyway?",
1231
+ initialValue: false
1232
+ });
1233
+ if (p3.isCancel(proceed) || !proceed) {
1234
+ p3.cancel("Upgrade cancelled.");
1235
+ return false;
1236
+ }
1237
+ return true;
1238
+ }
1239
+
1240
+ // src/upgrade.ts
1241
+ var TEMPLATE_REPO2 = "github:southwellmedia/velocity";
1242
+ var CLI_VERSION = "1.6.0";
1243
+ var FALLBACK_SAFE_FILES = [
1244
+ "src/components/ui/",
1245
+ "src/components/seo/",
1246
+ "src/components/layout/",
1247
+ "src/layouts/",
1248
+ "src/lib/",
1249
+ "src/styles/tokens/",
1250
+ "src/styles/global.css",
1251
+ "src/content.config.ts",
1252
+ "tsconfig.json",
1253
+ "eslint.config.js",
1254
+ ".prettierrc",
1255
+ ".prettierignore"
1256
+ ];
1257
+ function hasUncommittedChanges(targetDir) {
1258
+ try {
1259
+ const result = execSync("git status --porcelain", {
1085
1260
  cwd: targetDir,
1086
- stdio: "inherit",
1087
- // Stream output to terminal
1088
- shell: process.platform === "win32"
1089
- // Windows compatibility
1261
+ encoding: "utf-8"
1090
1262
  });
1091
- console.log(pc3.dim("\u2500".repeat(50)));
1092
- showSuccess("Dependencies installed");
1263
+ return result.trim().length > 0;
1264
+ } catch {
1265
+ return false;
1266
+ }
1267
+ }
1268
+ function isVersionLessThan(current, required) {
1269
+ const parse = (v) => v.replace(/^v/, "").split(/[-.]/).map((p6) => {
1270
+ const n = parseInt(p6, 10);
1271
+ return isNaN(n) ? 0 : n;
1272
+ });
1273
+ const a = parse(current);
1274
+ const b = parse(required);
1275
+ const len = Math.max(a.length, b.length);
1276
+ for (let i = 0; i < len; i++) {
1277
+ const av = a[i] ?? 0;
1278
+ const bv = b[i] ?? 0;
1279
+ if (av < bv) return true;
1280
+ if (av > bv) return false;
1281
+ }
1282
+ return false;
1283
+ }
1284
+ function scanForMigrationPatterns(targetDir, migrations) {
1285
+ const results = /* @__PURE__ */ new Map();
1286
+ for (const migration of migrations) {
1287
+ if (!migration.pattern) {
1288
+ results.set(migration.title, []);
1289
+ continue;
1290
+ }
1291
+ const regex = new RegExp(migration.pattern);
1292
+ const matches = [];
1293
+ const searchPaths = migration.searchPaths?.length ? migration.searchPaths : ["src/"];
1294
+ for (const searchPath of searchPaths) {
1295
+ const fullPath = join6(targetDir, searchPath);
1296
+ if (!existsSync7(fullPath)) continue;
1297
+ const files = walkFiles(fullPath);
1298
+ for (const file of files) {
1299
+ try {
1300
+ const content = readFileSync5(file, "utf-8");
1301
+ if (regex.test(content)) {
1302
+ matches.push(relative2(targetDir, file));
1303
+ }
1304
+ } catch {
1305
+ }
1306
+ }
1307
+ }
1308
+ results.set(migration.title, matches);
1309
+ }
1310
+ return results;
1311
+ }
1312
+ function walkFiles(dir) {
1313
+ const results = [];
1314
+ if (!existsSync7(dir)) return results;
1315
+ const stat = statSync3(dir);
1316
+ if (!stat.isDirectory()) {
1317
+ return [dir];
1318
+ }
1319
+ const entries = readdirSync4(dir, { withFileTypes: true });
1320
+ for (const entry of entries) {
1321
+ const fullPath = join6(dir, entry.name);
1322
+ if (entry.isDirectory()) {
1323
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
1324
+ results.push(...walkFiles(fullPath));
1325
+ } else {
1326
+ results.push(fullPath);
1327
+ }
1328
+ }
1329
+ return results;
1330
+ }
1331
+ function mergePackageJsonDeps(targetDir, manifest) {
1332
+ const pkgPath = join6(targetDir, "package.json");
1333
+ if (!existsSync7(pkgPath)) return;
1334
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
1335
+ for (const [name, version] of Object.entries(manifest.dependencies.update)) {
1336
+ if (pkg.dependencies?.[name] !== void 0) {
1337
+ pkg.dependencies[name] = version;
1338
+ } else if (pkg.devDependencies?.[name] !== void 0) {
1339
+ pkg.devDependencies[name] = version;
1340
+ } else {
1341
+ if (!pkg.dependencies) pkg.dependencies = {};
1342
+ pkg.dependencies[name] = version;
1343
+ }
1344
+ }
1345
+ for (const name of manifest.dependencies.remove) {
1346
+ if (pkg.dependencies?.[name] !== void 0) {
1347
+ delete pkg.dependencies[name];
1348
+ }
1349
+ if (pkg.devDependencies?.[name] !== void 0) {
1350
+ delete pkg.devDependencies[name];
1351
+ }
1352
+ }
1353
+ for (const [name, version] of Object.entries(manifest.dependencies.add)) {
1354
+ if (!pkg.dependencies) pkg.dependencies = {};
1355
+ pkg.dependencies[name] = version;
1356
+ }
1357
+ writeFileSync4(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
1358
+ }
1359
+ async function upgrade(options) {
1360
+ const { targetDir, dryRun, yes } = options;
1361
+ const config = readVelocityConfig(targetDir);
1362
+ if (!config) {
1363
+ p4.log.error(
1364
+ pc3.red(
1365
+ "This doesn't appear to be a Velocity project.\nRun this command from a project created with create-velocity-astro."
1366
+ )
1367
+ );
1368
+ process.exit(1);
1369
+ }
1370
+ if (hasUncommittedChanges(targetDir)) {
1371
+ const proceed = await warnDirtyGit(yes);
1372
+ if (!proceed) return;
1373
+ }
1374
+ const spinner3 = p4.spinner();
1375
+ spinner3.start("Downloading latest template...");
1376
+ const tempDir = join6(tmpdir(), `velocity-upgrade-${Date.now()}`);
1377
+ try {
1378
+ await downloadTemplate2(TEMPLATE_REPO2, {
1379
+ dir: tempDir,
1380
+ force: true
1381
+ });
1382
+ spinner3.stop("Template downloaded");
1093
1383
  } catch (error) {
1094
- console.log(pc3.dim("\u2500".repeat(50)));
1095
- showWarning("Failed to install dependencies");
1096
- const execaError = error;
1097
- if (execaError.message) {
1098
- console.log(pc3.dim(`
1099
- Error: ${execaError.message}`));
1384
+ spinner3.stop("Failed to download template");
1385
+ p4.log.error(
1386
+ pc3.red(
1387
+ "Could not download template. Check your internet connection.\n" + (error instanceof Error ? error.message : "")
1388
+ )
1389
+ );
1390
+ cleanup(tempDir);
1391
+ process.exit(1);
1392
+ }
1393
+ let manifest;
1394
+ const manifestPath = join6(tempDir, "velocity-manifest.json");
1395
+ if (existsSync7(manifestPath)) {
1396
+ manifest = readJson(manifestPath);
1397
+ } else {
1398
+ p4.log.warn(pc3.yellow("Manifest not found in template. Using fallback file list."));
1399
+ let templateVersion = config.version;
1400
+ const templatePkgPath = join6(tempDir, "package.json");
1401
+ if (existsSync7(templatePkgPath)) {
1402
+ const templatePkg = readJson(templatePkgPath);
1403
+ if (templatePkg.version) {
1404
+ templateVersion = templatePkg.version;
1405
+ }
1100
1406
  }
1101
- console.log();
1102
- console.log(pc3.yellow("To install manually, run:"));
1103
- console.log(` ${pc3.cyan(getInstallCommand(packageManager))}`);
1407
+ manifest = {
1408
+ version: templateVersion,
1409
+ minCliVersion: "1.0.0",
1410
+ files: {
1411
+ safe: FALLBACK_SAFE_FILES,
1412
+ protected: []
1413
+ },
1414
+ dependencies: {
1415
+ update: {},
1416
+ remove: [],
1417
+ add: {}
1418
+ },
1419
+ migrations: []
1420
+ };
1104
1421
  }
1105
- showSuccess(`Project "${projectName}" created successfully!`);
1422
+ if (isVersionLessThan(CLI_VERSION, manifest.minCliVersion)) {
1423
+ p4.log.error(
1424
+ pc3.red(
1425
+ `This upgrade requires create-velocity-astro >= ${manifest.minCliVersion}.
1426
+ Run \`npm update -g create-velocity-astro\` to update.`
1427
+ )
1428
+ );
1429
+ cleanup(tempDir);
1430
+ process.exit(1);
1431
+ }
1432
+ if (config.version === manifest.version) {
1433
+ showUpgradeIntro(config.version, manifest.version);
1434
+ p4.log.info(pc3.green(`Already on v${manifest.version}. Nothing to upgrade.`));
1435
+ p4.outro("");
1436
+ cleanup(tempDir);
1437
+ return;
1438
+ }
1439
+ const diffs = diffProjects(targetDir, tempDir, manifest);
1440
+ const { added, modified } = summarizeDiffs(diffs);
1441
+ if (added === 0 && modified === 0 && Object.keys(manifest.dependencies.update).length === 0 && manifest.dependencies.remove.length === 0 && Object.keys(manifest.dependencies.add).length === 0) {
1442
+ showUpgradeIntro(config.version, manifest.version);
1443
+ p4.log.info(pc3.green("All files are up to date. Updating version marker only."));
1444
+ if (!dryRun) {
1445
+ writeVelocityConfig(targetDir, {
1446
+ ...config,
1447
+ version: manifest.version,
1448
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
1449
+ });
1450
+ }
1451
+ p4.outro("");
1452
+ cleanup(tempDir);
1453
+ return;
1454
+ }
1455
+ showUpgradeIntro(config.version, manifest.version);
1456
+ showChangeSummary(diffs, manifest);
1457
+ const shouldProceed = await confirmUpgrade(dryRun);
1458
+ if (dryRun) {
1459
+ const matchResults2 = scanForMigrationPatterns(targetDir, manifest.migrations);
1460
+ showManualSteps(manifest.migrations, matchResults2);
1461
+ p4.outro(pc3.dim("Dry run complete. No changes were made."));
1462
+ cleanup(tempDir);
1463
+ return;
1464
+ }
1465
+ if (!shouldProceed) {
1466
+ cleanup(tempDir);
1467
+ return;
1468
+ }
1469
+ spinner3.start("Applying changes...");
1470
+ const changedDiffs = diffs.filter((d) => d.status === "added" || d.status === "modified");
1471
+ for (const diff of changedDiffs) {
1472
+ const src = join6(tempDir, diff.path);
1473
+ const dest = join6(targetDir, diff.path);
1474
+ const destDir = dirname4(dest);
1475
+ if (!existsSync7(destDir)) {
1476
+ mkdirSync4(destDir, { recursive: true });
1477
+ }
1478
+ copyFileSync3(src, dest);
1479
+ }
1480
+ const hasDepChanges = Object.keys(manifest.dependencies.update).length > 0 || manifest.dependencies.remove.length > 0 || Object.keys(manifest.dependencies.add).length > 0;
1481
+ if (hasDepChanges) {
1482
+ mergePackageJsonDeps(targetDir, manifest);
1483
+ }
1484
+ writeVelocityConfig(targetDir, {
1485
+ ...config,
1486
+ version: manifest.version,
1487
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
1488
+ });
1489
+ spinner3.stop("Changes applied");
1490
+ if (modified > 0) {
1491
+ p4.log.success(pc3.green(`Updated ${modified} framework file${modified !== 1 ? "s" : ""}`));
1492
+ }
1493
+ if (added > 0) {
1494
+ p4.log.success(pc3.green(`Added ${added} new file${added !== 1 ? "s" : ""}`));
1495
+ }
1496
+ if (hasDepChanges) {
1497
+ p4.log.success(pc3.green("Updated package.json dependencies"));
1498
+ }
1499
+ p4.log.success(pc3.green("Updated .velocity.json"));
1500
+ const matchResults = scanForMigrationPatterns(targetDir, manifest.migrations);
1501
+ showManualSteps(manifest.migrations, matchResults);
1502
+ showUpgradeOutro(hasDepChanges);
1503
+ cleanup(tempDir);
1106
1504
  }
1107
-
1108
- // src/utils/fs.ts
1109
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync, copyFileSync as copyFileSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1110
- import { join as join3, dirname as dirname3 } from "path";
1111
- function isEmptyDir(path) {
1112
- if (!existsSync4(path)) return true;
1113
- const files = readdirSync2(path);
1114
- return files.length === 0 || files.length === 1 && files[0] === ".git";
1505
+ function cleanup(tempDir) {
1506
+ try {
1507
+ if (existsSync7(tempDir)) {
1508
+ rmSync2(tempDir, { recursive: true, force: true });
1509
+ }
1510
+ } catch {
1511
+ }
1115
1512
  }
1116
1513
 
1117
1514
  // src/cli.ts
1118
1515
  var HELP_TEXT = `
1119
- ${pc4.bold("create-velocity-astro")} - Create a new Velocity project
1516
+ ${pc4.bold("create-velocity-astro")} - Create and upgrade Velocity projects
1120
1517
 
1121
1518
  ${pc4.bold("Usage:")}
1122
1519
  npm create velocity-astro@latest [project-name] [options]
1123
1520
  pnpm create velocity-astro [project-name] [options]
1124
- yarn create velocity-astro [project-name] [options]
1125
- bun create velocity-astro [project-name] [options]
1521
+
1522
+ ${pc4.bold("Commands:")}
1523
+ ${pc4.cyan("upgrade")} Upgrade an existing Velocity project to the latest version
1524
+ --dry-run Preview changes without applying them
1525
+ --yes, -y Skip confirmation prompts
1126
1526
 
1127
1527
  ${pc4.bold("Options:")}
1128
1528
  --demo Include demo landing page and sample content
@@ -1144,11 +1544,13 @@ ${pc4.bold("Examples:")}
1144
1544
  npm create velocity-astro@latest my-site
1145
1545
  npm create velocity-astro@latest my-site --demo --components
1146
1546
  npm create velocity-astro@latest my-site --components=ui,patterns
1147
- npm create velocity-astro@latest my-site --components=none
1148
- npm create velocity-astro@latest my-site --i18n --pages
1149
1547
  pnpm create velocity-astro my-site -y
1548
+
1549
+ ${pc4.dim("# Upgrade an existing project")}
1550
+ pnpm create velocity-astro upgrade
1551
+ pnpm create velocity-astro upgrade --dry-run
1150
1552
  `;
1151
- var VERSION = "1.4.2";
1553
+ var VERSION = "1.6.0";
1152
1554
  function parseComponentsFlag(value) {
1153
1555
  if (value === void 0) {
1154
1556
  return void 0;
@@ -1175,7 +1577,7 @@ function parseComponentsFlag(value) {
1175
1577
  }
1176
1578
  async function run(argv) {
1177
1579
  const args = mri(argv, {
1178
- boolean: ["demo", "i18n", "pages", "help", "version", "yes"],
1580
+ boolean: ["demo", "i18n", "pages", "help", "version", "yes", "dry-run"],
1179
1581
  string: ["components"],
1180
1582
  alias: {
1181
1583
  h: "help",
@@ -1191,13 +1593,23 @@ async function run(argv) {
1191
1593
  console.log(VERSION);
1192
1594
  return;
1193
1595
  }
1596
+ const subcommand = args._[0];
1597
+ if (subcommand === "upgrade") {
1598
+ const targetDir2 = resolve2(process.cwd());
1599
+ await upgrade({
1600
+ targetDir: targetDir2,
1601
+ dryRun: args["dry-run"] || false,
1602
+ yes: args.yes || false
1603
+ });
1604
+ return;
1605
+ }
1194
1606
  showIntro();
1195
1607
  const argProjectName = args._[0];
1196
1608
  const componentSelection = parseComponentsFlag(args.components);
1197
1609
  if (args.yes) {
1198
1610
  const projectName2 = toValidProjectName(argProjectName || "my-velocity-site");
1199
1611
  const targetDir2 = resolve2(process.cwd(), projectName2);
1200
- if (existsSync5(targetDir2) && !isEmptyDir(targetDir2)) {
1612
+ if (existsSync8(targetDir2) && !isEmptyDir(targetDir2)) {
1201
1613
  showError(`Directory "${projectName2}" already exists and is not empty.`);
1202
1614
  process.exit(1);
1203
1615
  }
@@ -1226,13 +1638,13 @@ async function run(argv) {
1226
1638
  }
1227
1639
  const { projectName, demo, componentSelection: selectedComponents, i18n, pages, pageLayout, packageManager } = answers;
1228
1640
  const targetDir = resolve2(process.cwd(), projectName);
1229
- if (existsSync5(targetDir) && !isEmptyDir(targetDir)) {
1230
- const shouldOverwrite = await p3.confirm({
1641
+ if (existsSync8(targetDir) && !isEmptyDir(targetDir)) {
1642
+ const shouldOverwrite = await p5.confirm({
1231
1643
  message: `Directory "${projectName}" already exists. Continue and overwrite?`,
1232
1644
  initialValue: false
1233
1645
  });
1234
- if (!shouldOverwrite || p3.isCancel(shouldOverwrite)) {
1235
- p3.cancel("Operation cancelled.");
1646
+ if (!shouldOverwrite || p5.isCancel(shouldOverwrite)) {
1647
+ p5.cancel("Operation cancelled.");
1236
1648
  process.exit(0);
1237
1649
  }
1238
1650
  }