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 +627 -215
- package/dist/index.js.map +1 -1
- package/package.json +1 -4
- package/templates/i18n/src/components/hero/Hero.astro +154 -154
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
|
|
7
|
-
import * as
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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(
|
|
467
|
+
p.log.error(pc.red(message));
|
|
501
468
|
}
|
|
502
469
|
function showWarning(message) {
|
|
503
|
-
p.log.warn(
|
|
470
|
+
p.log.warn(pc.yellow(message));
|
|
504
471
|
}
|
|
505
472
|
function showSuccess(message) {
|
|
506
|
-
p.log.success(
|
|
473
|
+
p.log.success(pc.green(message));
|
|
507
474
|
}
|
|
508
475
|
|
|
509
476
|
// src/scaffold.ts
|
|
510
|
-
import { existsSync as
|
|
511
|
-
import { join as
|
|
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}: '${
|
|
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: '${
|
|
659
|
-
description: '${
|
|
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 =
|
|
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
|
|
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 (!
|
|
872
|
-
|
|
854
|
+
if (!existsSync5(dest)) {
|
|
855
|
+
mkdirSync3(dest, { recursive: true });
|
|
873
856
|
}
|
|
874
|
-
const entries =
|
|
857
|
+
const entries = readdirSync2(src, { withFileTypes: true });
|
|
875
858
|
for (const entry of entries) {
|
|
876
|
-
const srcPath =
|
|
877
|
-
const destPath =
|
|
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
|
-
|
|
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 =
|
|
888
|
-
if (
|
|
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 =
|
|
904
|
-
if (!
|
|
905
|
-
const entries =
|
|
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(
|
|
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 =
|
|
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 =
|
|
928
|
-
if (!
|
|
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(
|
|
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
|
-
|
|
920
|
+
writeFileSync3(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
938
921
|
}
|
|
939
922
|
function applyBaseTemplate(targetDir) {
|
|
940
923
|
const baseTemplate = getBaseTemplatePath();
|
|
941
|
-
if (
|
|
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 =
|
|
965
|
-
if (!
|
|
966
|
-
|
|
967
|
-
|
|
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 =
|
|
991
|
-
const cnDir =
|
|
992
|
-
if (!
|
|
993
|
-
|
|
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
|
|
1000
|
-
|
|
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
|
-
|
|
980
|
+
spinner3.stop("Template downloaded");
|
|
1008
981
|
} catch (error) {
|
|
1009
|
-
|
|
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
|
-
|
|
989
|
+
spinner3.start("Configuring components...");
|
|
1017
990
|
try {
|
|
1018
991
|
await applyComponentSelection(targetDir, componentSelection);
|
|
1019
|
-
|
|
992
|
+
spinner3.stop("Components configured");
|
|
1020
993
|
} catch (error) {
|
|
1021
|
-
|
|
994
|
+
spinner3.stop("Failed to configure components");
|
|
1022
995
|
throw error;
|
|
1023
996
|
}
|
|
1024
997
|
}
|
|
1025
998
|
if (i18n) {
|
|
1026
|
-
|
|
999
|
+
spinner3.start("Adding i18n support...");
|
|
1027
1000
|
try {
|
|
1028
1001
|
applyI18nOverlay(targetDir);
|
|
1029
|
-
|
|
1002
|
+
spinner3.stop("i18n support added");
|
|
1030
1003
|
} catch (error) {
|
|
1031
|
-
|
|
1004
|
+
spinner3.stop("Failed to add i18n support");
|
|
1032
1005
|
throw error;
|
|
1033
1006
|
}
|
|
1034
1007
|
}
|
|
1035
1008
|
if (!demo) {
|
|
1036
|
-
|
|
1009
|
+
spinner3.start("Configuring minimal template...");
|
|
1037
1010
|
removeItems(targetDir, DEMO_CONTENT);
|
|
1038
1011
|
applyBaseTemplate(targetDir);
|
|
1039
1012
|
createContentDirectories(targetDir);
|
|
1040
|
-
|
|
1041
|
-
spinner2.stop("Minimal template configured");
|
|
1013
|
+
spinner3.stop("Minimal template configured");
|
|
1042
1014
|
}
|
|
1043
1015
|
if (pages.length > 0) {
|
|
1044
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1021
|
+
spinner3.stop("Failed to generate pages");
|
|
1060
1022
|
throw error;
|
|
1061
1023
|
}
|
|
1062
1024
|
}
|
|
1063
|
-
|
|
1025
|
+
spinner3.start("Configuring project...");
|
|
1064
1026
|
try {
|
|
1065
1027
|
updatePackageJson(targetDir, projectName);
|
|
1066
|
-
|
|
1028
|
+
spinner3.stop("Project configured");
|
|
1067
1029
|
} catch (error) {
|
|
1068
|
-
|
|
1030
|
+
spinner3.stop("Failed to configure project");
|
|
1069
1031
|
throw error;
|
|
1070
1032
|
}
|
|
1071
|
-
|
|
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
|
-
|
|
1051
|
+
spinner3.stop("Git repository initialized");
|
|
1075
1052
|
} else {
|
|
1076
|
-
|
|
1053
|
+
spinner3.stop("Git not available, skipping");
|
|
1077
1054
|
}
|
|
1078
|
-
|
|
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
|
-
|
|
1087
|
-
// Stream output to terminal
|
|
1088
|
-
shell: process.platform === "win32"
|
|
1089
|
-
// Windows compatibility
|
|
1261
|
+
encoding: "utf-8"
|
|
1090
1262
|
});
|
|
1091
|
-
|
|
1092
|
-
|
|
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
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
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
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
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
|
|
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
|
-
|
|
1125
|
-
|
|
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.
|
|
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 (
|
|
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 (
|
|
1230
|
-
const shouldOverwrite = await
|
|
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 ||
|
|
1235
|
-
|
|
1646
|
+
if (!shouldOverwrite || p5.isCancel(shouldOverwrite)) {
|
|
1647
|
+
p5.cancel("Operation cancelled.");
|
|
1236
1648
|
process.exit(0);
|
|
1237
1649
|
}
|
|
1238
1650
|
}
|