Add Biome and Husky for code formatting and Git hooks

This commit is contained in:
Aman Varshney
2025-03-22 22:51:59 +05:30
parent 0790fd0894
commit 996b35b78a
37 changed files with 609 additions and 437 deletions

View File

@@ -19,6 +19,66 @@ export async function setupAddons(
if (addons.includes("tauri")) {
await setupTauri(projectDir, packageManager);
}
if (addons.includes("biome")) {
await setupBiome(projectDir);
}
if (addons.includes("husky")) {
await setupHusky(projectDir);
}
}
async function setupBiome(projectDir: string) {
const biomeTemplateDir = path.join(PKG_ROOT, "template/with-biome");
if (await fs.pathExists(biomeTemplateDir)) {
await fs.copy(biomeTemplateDir, projectDir, { overwrite: true });
}
addPackageDependency({
devDependencies: ["@biomejs/biome"],
projectDir,
});
const packageJsonPath = path.join(projectDir, "package.json");
if (await fs.pathExists(packageJsonPath)) {
const packageJson = await fs.readJson(packageJsonPath);
packageJson.scripts = {
...packageJson.scripts,
check: "biome check --write .",
};
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
}
}
async function setupHusky(projectDir: string) {
const huskyTemplateDir = path.join(PKG_ROOT, "template/with-husky");
if (await fs.pathExists(huskyTemplateDir)) {
await fs.copy(huskyTemplateDir, projectDir, { overwrite: true });
}
addPackageDependency({
devDependencies: ["husky", "lint-staged"],
projectDir,
});
const packageJsonPath = path.join(projectDir, "package.json");
if (await fs.pathExists(packageJsonPath)) {
const packageJson = await fs.readJson(packageJsonPath);
packageJson.scripts = {
...packageJson.scripts,
prepare: "husky",
};
packageJson["lint-staged"] = {
"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
"biome check --no-errors-on-unmatched --files-ignore-unknown=true",
],
};
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
}
}
async function setupDocker(projectDir: string) {

View File

@@ -1,17 +1,18 @@
import { log, spinner } from "@clack/prompts";
import { $ } from "execa";
import pc from "picocolors";
import type { ProjectAddons } from "../types";
import type { PackageManager } from "../utils/get-package-manager";
interface InstallDependenciesOptions {
projectDir: string;
packageManager: PackageManager;
}
export async function installDependencies({
projectDir,
packageManager,
}: InstallDependenciesOptions) {
addons = [],
}: {
projectDir: string;
packageManager: PackageManager;
addons?: ProjectAddons[];
}) {
const s = spinner();
try {
@@ -34,6 +35,11 @@ export async function installDependencies({
}
s.stop("Dependencies installed successfully");
// Run Biome check if Biome or Husky is enabled
if (addons.includes("biome") || addons.includes("husky")) {
await runBiomeCheck(projectDir, packageManager);
}
} catch (error) {
s.stop(pc.red("Failed to install dependencies"));
if (error instanceof Error) {
@@ -42,3 +48,21 @@ export async function installDependencies({
throw error;
}
}
async function runBiomeCheck(
projectDir: string,
packageManager: PackageManager,
) {
const s = spinner();
try {
s.start("Running Biome format check...");
await $({ cwd: projectDir })`${packageManager} biome check --write .`;
s.stop("Biome check completed successfully");
} catch (error) {
s.stop(pc.yellow("Biome check encountered issues"));
log.warn(pc.yellow("Some files may need manual formatting"));
}
}

View File

@@ -17,6 +17,17 @@ export function displayPostInstallInstructions(
) {
const runCmd = packageManager === "npm" ? "npm run" : packageManager;
const cdCmd = `cd ${projectName}`;
const hasHuskyOrBiome =
addons?.includes("husky") || addons?.includes("biome");
const databaseInstructions =
database !== "none" ? getDatabaseInstructions(database, orm, runCmd) : "";
const tauriInstructions = addons?.includes("tauri")
? getTauriInstructions(runCmd)
: "";
const lintingInstructions = hasHuskyOrBiome
? getLintingInstructions(runCmd)
: "";
log.info(`${pc.cyan("Project created successfully!")}
@@ -27,9 +38,11 @@ ${!depsInstalled ? `${pc.cyan("2.")} ${packageManager} install\n` : ""}${pc.cyan
${pc.bold("Your project will be available at:")}
${pc.cyan("•")} Frontend: http://localhost:3001
${pc.cyan("•")} API: http://localhost:3000
${databaseInstructions ? `\n${databaseInstructions.trim()}` : ""}${tauriInstructions ? `\n${tauriInstructions.trim()}` : ""}${lintingInstructions ? `\n${lintingInstructions.trim()}` : ""}`);
}
${database !== "none" ? getDatabaseInstructions(database, orm, runCmd) : ""}
${addons?.includes("tauri") ? getTauriInstructions(runCmd) : ""}`);
function getLintingInstructions(runCmd?: string): string {
return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${pc.dim(`${runCmd} check`)}\n\n`;
}
function getDatabaseInstructions(