feat: add command (#337)

This commit is contained in:
Aman Varshney
2025-06-22 03:20:05 +05:30
committed by GitHub
parent 198d0e7434
commit 9c7a0f0110
29 changed files with 1015 additions and 255 deletions

View File

@@ -1,7 +1,11 @@
import { cancel, isCancel, multiselect } from "@clack/prompts";
import pc from "picocolors";
import { DEFAULT_CONFIG } from "../constants";
import type { Addons, Frontend } from "../types";
import { type Addons, AddonsSchema, type Frontend } from "../types";
import {
getCompatibleAddons,
validateAddonCompatibility,
} from "../utils/addon-compatibility";
type AddonOption = {
value: Addons;
@@ -9,63 +13,73 @@ type AddonOption = {
hint: string;
};
function getAddonDisplay(
addon: Addons,
isRecommended = false,
): { label: string; hint: string } {
let label: string;
let hint: string;
if (addon === "turborepo") {
label = isRecommended ? "Turborepo (Recommended)" : "Turborepo";
hint = "High-performance build system for JavaScript and TypeScript";
} else if (addon === "pwa") {
label = "PWA (Progressive Web App)";
hint = "Make your app installable and work offline";
} else if (addon === "tauri") {
label = isRecommended ? "Tauri Desktop App" : "Tauri";
hint = "Build native desktop apps from your web frontend";
} else if (addon === "biome") {
label = "Biome";
hint = isRecommended
? "Add Biome for linting and formatting"
: "Fast formatter and linter for JavaScript, TypeScript, JSX";
} else if (addon === "husky") {
label = "Husky";
hint = isRecommended
? "Add Git hooks with Husky, lint-staged (requires Biome)"
: "Git hooks made easy";
} else if (addon === "starlight") {
label = "Starlight";
hint = isRecommended
? "Add Astro Starlight documentation site"
: "Documentation site with Astro";
} else {
label = addon;
hint = `Add ${addon}`;
}
return { label, hint };
}
export async function getAddonsChoice(
addons?: Addons[],
frontends?: Frontend[],
): Promise<Addons[]> {
if (addons !== undefined) return addons;
const hasCompatiblePwaFrontend =
frontends?.includes("react-router") ||
frontends?.includes("tanstack-router") ||
frontends?.includes("solid") ||
frontends?.includes("next");
const allAddons = AddonsSchema.options.filter((addon) => addon !== "none");
const hasCompatibleTauriFrontend =
frontends?.includes("react-router") ||
frontends?.includes("tanstack-router") ||
frontends?.includes("nuxt") ||
frontends?.includes("svelte") ||
frontends?.includes("solid") ||
frontends?.includes("next");
const allPossibleOptions: AddonOption[] = [];
const allPossibleOptions: AddonOption[] = [
{
value: "turborepo",
label: "Turborepo (Recommended)",
hint: "Optimize builds for monorepos",
},
{
value: "starlight",
label: "Starlight",
hint: "Add Astro Starlight documentation site",
},
{
value: "biome",
label: "Biome",
hint: "Add Biome for linting and formatting",
},
{
value: "husky",
label: "Husky",
hint: "Add Git hooks with Husky, lint-staged (requires Biome)",
},
{
value: "pwa",
label: "PWA (Progressive Web App)",
hint: "Make your app installable and work offline",
},
{
value: "tauri",
label: "Tauri Desktop App",
hint: "Build native desktop apps from your web frontend",
},
];
for (const addon of allAddons) {
const { isCompatible } = validateAddonCompatibility(addon, frontends || []);
const options = allPossibleOptions.filter((option) => {
if (option.value === "pwa") return hasCompatiblePwaFrontend;
if (option.value === "tauri") return hasCompatibleTauriFrontend;
return true;
if (isCompatible) {
const { label, hint } = getAddonDisplay(addon, true);
allPossibleOptions.push({
value: addon,
label,
hint,
});
}
}
const options = allPossibleOptions.sort((a, b) => {
if (a.value === "turborepo") return -1;
if (b.value === "turborepo") return 1;
return 0;
});
const initialValues = DEFAULT_CONFIG.addons.filter((addonValue) =>
@@ -90,3 +104,45 @@ export async function getAddonsChoice(
return response;
}
export async function getAddonsToAdd(
frontend: Frontend[],
existingAddons: Addons[] = [],
): Promise<Addons[]> {
const options: AddonOption[] = [];
const allAddons = AddonsSchema.options.filter((addon) => addon !== "none");
const compatibleAddons = getCompatibleAddons(
allAddons,
frontend,
existingAddons,
);
for (const addon of compatibleAddons) {
const { label, hint } = getAddonDisplay(addon, false);
options.push({
value: addon,
label,
hint,
});
}
if (options.length === 0) {
return [];
}
const response = await multiselect<Addons>({
message: "Select addons",
options: options,
required: true,
});
if (isCancel(response)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
return response;
}