feat(cli): use biome js api to format all generated templates (#571)

This commit is contained in:
Aman Varshney
2025-09-11 01:28:56 +05:30
committed by GitHub
parent 5e91f79b29
commit ce97790e54
9 changed files with 293 additions and 121 deletions

View File

@@ -1,88 +1,90 @@
{
"name": "create-better-t-stack",
"version": "2.43.1",
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
"type": "module",
"license": "MIT",
"author": "Aman Varshney",
"bin": {
"create-better-t-stack": "dist/cli.js"
},
"files": [
"templates",
"dist"
],
"keywords": [
"better-t-stack",
"typescript",
"boilerplate",
"starter",
"cli",
"turborepo",
"trpc",
"better-auth",
"monorepo",
"fullstack",
"type-safety",
"react",
"react-native",
"expo",
"hono",
"elysia",
"drizzle",
"prisma",
"tanstack",
"tailwind",
"shadcn",
"pwa",
"tauri",
"biome"
],
"repository": {
"type": "git",
"url": "git+https://github.com/AmanVarshney01/create-better-t-stack.git",
"directory": "apps/cli"
},
"publishConfig": {
"access": "public"
},
"homepage": "https://better-t-stack.dev/",
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"check-types": "tsc --noEmit",
"check": "biome check --write .",
"test": "bun run build && vitest run",
"test:ui": "bun run build && vitest --ui",
"test:with-build": "bun run build && WITH_BUILD=1 vitest --ui",
"prepublishOnly": "npm run build"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"dependencies": {
"@clack/prompts": "^1.0.0-alpha.4",
"consola": "^3.4.2",
"execa": "^9.6.0",
"fs-extra": "^11.3.1",
"gradient-string": "^3.0.0",
"handlebars": "^4.7.8",
"jsonc-parser": "^3.3.1",
"picocolors": "^1.1.1",
"tinyglobby": "^0.2.15",
"trpc-cli": "^0.10.2",
"ts-morph": "^27.0.0",
"zod": "^4.1.5"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/node": "^24.3.1",
"@vitest/ui": "^3.2.4",
"tsdown": "^0.14.2",
"typescript": "^5.9.2",
"vitest": "^3.2.4"
}
"name": "create-better-t-stack",
"version": "2.43.1",
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
"type": "module",
"license": "MIT",
"author": "Aman Varshney",
"bin": {
"create-better-t-stack": "dist/cli.js"
},
"files": [
"templates",
"dist"
],
"keywords": [
"better-t-stack",
"typescript",
"boilerplate",
"starter",
"cli",
"turborepo",
"trpc",
"better-auth",
"monorepo",
"fullstack",
"type-safety",
"react",
"react-native",
"expo",
"hono",
"elysia",
"drizzle",
"prisma",
"tanstack",
"tailwind",
"shadcn",
"pwa",
"tauri",
"biome"
],
"repository": {
"type": "git",
"url": "git+https://github.com/AmanVarshney01/create-better-t-stack.git",
"directory": "apps/cli"
},
"publishConfig": {
"access": "public"
},
"homepage": "https://better-t-stack.dev/",
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"check-types": "tsc --noEmit",
"check": "biome check --write .",
"test": "bun run build && vitest run",
"test:ui": "bun run build && vitest --ui",
"test:with-build": "bun run build && WITH_BUILD=1 vitest --ui",
"prepublishOnly": "npm run build"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"dependencies": {
"@biomejs/js-api": "^3.0.0",
"@biomejs/wasm-nodejs": "^2.2.4",
"@clack/prompts": "^1.0.0-alpha.4",
"consola": "^3.4.2",
"execa": "^9.6.0",
"fs-extra": "^11.3.1",
"gradient-string": "^3.0.0",
"handlebars": "^4.7.8",
"jsonc-parser": "^3.3.1",
"picocolors": "^1.1.1",
"tinyglobby": "^0.2.15",
"trpc-cli": "^0.10.2",
"ts-morph": "^27.0.0",
"zod": "^4.1.5"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/node": "^24.3.1",
"@vitest/ui": "^3.2.4",
"tsdown": "^0.14.2",
"typescript": "^5.9.2",
"vitest": "^3.2.4"
}
}

View File

@@ -92,7 +92,7 @@ export const dependencyVersionMap = {
"@elysiajs/cors": "^1.3.3",
"@elysiajs/trpc": "^1.1.0",
"elysia": "^1.3.21",
elysia: "^1.3.21",
"@hono/node-server": "^1.14.4",
"@hono/trpc-server": "^0.4.0",
@@ -108,7 +108,7 @@ export const dependencyVersionMap = {
turbo: "^2.5.4",
"ai": "^5.0.39",
ai: "^5.0.39",
"@ai-sdk/google": "^2.0.13",
"@ai-sdk/vue": "^2.0.39",
"@ai-sdk/svelte": "^3.0.39",

View File

@@ -47,11 +47,7 @@ export async function processAndCopyFiles(
continue;
}
if (srcPath.endsWith(".hbs")) {
await processTemplate(srcPath, destPath, context);
} else {
await fs.copy(srcPath, destPath, { overwrite: true });
}
await processTemplate(srcPath, destPath, context);
}
}

View File

@@ -13,7 +13,12 @@ export async function setupNextAlchemyDeploy(
await addPackageDependency({
dependencies: ["@opennextjs/cloudflare"],
devDependencies: ["alchemy", "dotenv", "wrangler", "@cloudflare/workers-types"],
devDependencies: [
"alchemy",
"dotenv",
"wrangler",
"@cloudflare/workers-types",
],
projectDir: webAppDir,
});

View File

@@ -0,0 +1,95 @@
import path from "node:path";
import { Biome } from "@biomejs/js-api/nodejs";
import consola from "consola";
let biome: Biome | null = null;
let projectKey: number | null = null;
async function initializeBiome(): Promise<{
biome: Biome;
projectKey: number;
}> {
if (biome && projectKey !== null) return { biome, projectKey };
try {
biome = new Biome();
const result = biome.openProject("./");
projectKey = result.projectKey;
biome.applyConfiguration(projectKey, {
formatter: {
enabled: true,
indentStyle: "tab",
indentWidth: 2,
lineWidth: 80,
},
linter: {
enabled: false,
},
javascript: {
formatter: {
enabled: true,
},
},
json: {
formatter: {
enabled: true,
},
},
});
return { biome, projectKey };
} catch (error) {
consola.error("Failed to initialize Biome:", error);
throw error;
}
}
function isSupportedFile(filePath: string): boolean {
const ext = path.extname(filePath).toLowerCase();
const supportedExtensions = [".js", ".jsx", ".ts", ".tsx", ".json", ".jsonc"];
return supportedExtensions.includes(ext);
}
function shouldSkipFile(filePath: string): boolean {
const basename = path.basename(filePath);
const skipPatterns = [
".hbs",
"package-lock.json",
"yarn.lock",
"pnpm-lock.yaml",
"bun.lock",
".d.ts",
];
return skipPatterns.some((pattern) => basename.includes(pattern));
}
export async function formatFileWithBiome(
filePath: string,
content: string,
): Promise<string | null> {
if (!isSupportedFile(filePath) || shouldSkipFile(filePath)) {
return null;
}
try {
const { biome: biomeInstance, projectKey: key } = await initializeBiome();
const result = biomeInstance.formatContent(key, content, {
filePath: path.basename(filePath),
});
if (result.diagnostics && result.diagnostics.length > 0) {
consola.debug(
`Biome formatting diagnostics for ${filePath}:`,
result.diagnostics,
);
}
return result.content;
} catch (error) {
consola.warn(`Failed to format ${filePath} with Biome:`, error);
return null;
}
}

View File

@@ -3,25 +3,36 @@ import consola from "consola";
import fs from "fs-extra";
import handlebars from "handlebars";
import type { ProjectConfig } from "../types";
import { formatFileWithBiome } from "./biome-formatter";
/**
* Processes a Handlebars template file and writes the output to the destination.
* @param srcPath Path to the source .hbs template file.
* @param destPath Path to write the processed file.
* @param context Data to be passed to the Handlebars template.
*/
export async function processTemplate(
srcPath: string,
destPath: string,
context: ProjectConfig,
) {
try {
const templateContent = await fs.readFile(srcPath, "utf-8");
const template = handlebars.compile(templateContent);
const processedContent = template(context);
await fs.ensureDir(path.dirname(destPath));
await fs.writeFile(destPath, processedContent);
let content: string;
if (srcPath.endsWith(".hbs")) {
const templateContent = await fs.readFile(srcPath, "utf-8");
const template = handlebars.compile(templateContent);
content = template(context);
} else {
content = await fs.readFile(srcPath, "utf-8");
}
try {
const formattedContent = await formatFileWithBiome(destPath, content);
if (formattedContent) {
content = formattedContent;
}
} catch (formatError) {
consola.debug(`Failed to format ${destPath}:`, formatError);
}
await fs.writeFile(destPath, content);
} catch (error) {
consola.error(`Error processing template ${srcPath}:`, error);
throw new Error(`Failed to process template ${srcPath}`);