mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
Add Biome and Husky for code formatting and Git hooks
This commit is contained in:
@@ -33,6 +33,11 @@ export const dependencyVersionMap = {
|
||||
"@vite-pwa/assets-generator": "^0.2.6",
|
||||
|
||||
"@tauri-apps/cli": "^2.4.0",
|
||||
|
||||
"@biomejs/biome": "1.9.4",
|
||||
|
||||
husky: "^9.1.7",
|
||||
"lint-staged": "^15.5.0",
|
||||
} as const;
|
||||
|
||||
export type AvailableDependencies = keyof typeof dependencyVersionMap;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -33,6 +33,8 @@ async function main() {
|
||||
.option("--docker", "Include Docker setup")
|
||||
.option("--pwa", "Include Progressive Web App support")
|
||||
.option("--tauri", "Include Tauri desktop app support")
|
||||
.option("--biome", "Include Biome for linting and formatting")
|
||||
.option("--husky", "Include Husky, lint-staged for Git hooks")
|
||||
.option("--no-addons", "Skip all additional addons")
|
||||
.option("--git", "Include git setup")
|
||||
.option("--no-git", "Skip git initialization")
|
||||
@@ -75,6 +77,8 @@ async function main() {
|
||||
...((options.docker ||
|
||||
options.pwa ||
|
||||
options.tauri ||
|
||||
options.biome ||
|
||||
options.husky ||
|
||||
options.addons === false) && {
|
||||
addons:
|
||||
options.addons === false
|
||||
@@ -83,6 +87,8 @@ async function main() {
|
||||
...(options.docker ? ["docker"] : []),
|
||||
...(options.pwa ? ["pwa"] : []),
|
||||
...(options.tauri ? ["tauri"] : []),
|
||||
...(options.biome ? ["biome"] : []),
|
||||
...(options.husky ? ["husky"] : []),
|
||||
] as ProjectAddons[]),
|
||||
}),
|
||||
};
|
||||
@@ -141,6 +147,7 @@ async function main() {
|
||||
await installDependencies({
|
||||
projectDir,
|
||||
packageManager: config.packageManager,
|
||||
addons: config.addons,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,16 @@ export async function getAddonsChoice(
|
||||
label: "Tauri Desktop App",
|
||||
hint: "Build native desktop apps from your web frontend",
|
||||
},
|
||||
{
|
||||
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)",
|
||||
},
|
||||
],
|
||||
required: false,
|
||||
});
|
||||
@@ -34,5 +44,9 @@ export async function getAddonsChoice(
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (response.includes("husky") && !response.includes("biome")) {
|
||||
response.push("biome");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export type ProjectDatabase = "sqlite" | "postgres" | "none";
|
||||
export type ProjectOrm = "drizzle" | "prisma" | "none";
|
||||
export type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
|
||||
export type ProjectAddons = "docker" | "pwa" | "tauri";
|
||||
export type ProjectAddons = "docker" | "pwa" | "tauri" | "biome" | "husky";
|
||||
|
||||
export interface ProjectConfig {
|
||||
projectName: string;
|
||||
|
||||
Reference in New Issue
Block a user