mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
feat(cli): add ultracite, oxlint, fumadocs addons (#427)
This commit is contained in:
@@ -129,11 +129,15 @@ const getBadgeColors = (category: string): string => {
|
||||
}
|
||||
};
|
||||
|
||||
const TechIcon: React.FC<{
|
||||
function TechIcon({
|
||||
icon,
|
||||
name,
|
||||
className,
|
||||
}: {
|
||||
icon: string;
|
||||
name: string;
|
||||
className?: string;
|
||||
}> = ({ icon, name, className }) => {
|
||||
}) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const { theme } = useTheme();
|
||||
|
||||
@@ -168,7 +172,7 @@ const TechIcon: React.FC<{
|
||||
{icon}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const getCategoryDisplayName = (categoryKey: string): string => {
|
||||
const result = categoryKey.replace(/([A-Z])/g, " $1");
|
||||
@@ -754,10 +758,36 @@ const analyzeStackCompatibility = (stack: StackState): CompatibilityResult => {
|
||||
|
||||
if (
|
||||
nextStack.addons.includes("husky") &&
|
||||
!nextStack.addons.includes("biome")
|
||||
!nextStack.addons.includes("biome") &&
|
||||
!nextStack.addons.includes("oxlint")
|
||||
) {
|
||||
notes.addons.notes.push(
|
||||
"Husky addon is selected without Biome. Consider adding Biome for lint-staged integration.",
|
||||
"Husky addon is selected without a linter. Consider adding Biome or Oxlint for lint-staged integration.",
|
||||
);
|
||||
}
|
||||
|
||||
if (nextStack.addons.includes("ultracite")) {
|
||||
if (nextStack.addons.includes("biome")) {
|
||||
notes.addons.notes.push(
|
||||
"Ultracite includes Biome setup. Biome addon will be removed.",
|
||||
);
|
||||
nextStack.addons = nextStack.addons.filter(
|
||||
(addon) => addon !== "biome",
|
||||
);
|
||||
changed = true;
|
||||
changes.push({
|
||||
category: "addons",
|
||||
message: "Biome addon removed (included in Ultracite)",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
nextStack.addons.includes("oxlint") &&
|
||||
nextStack.addons.includes("biome")
|
||||
) {
|
||||
notes.addons.notes.push(
|
||||
"Both Oxlint and Biome are selected. Consider using only one linter.",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -967,7 +997,26 @@ const generateCommand = (stackState: StackState): string => {
|
||||
|
||||
if (!checkDefault("addons", stackState.addons)) {
|
||||
if (stackState.addons.length > 0) {
|
||||
flags.push(`--addons ${stackState.addons.join(" ")}`);
|
||||
const validAddons = stackState.addons.filter((addon) =>
|
||||
[
|
||||
"pwa",
|
||||
"tauri",
|
||||
"starlight",
|
||||
"biome",
|
||||
"husky",
|
||||
"turborepo",
|
||||
"ultracite",
|
||||
"fumadocs",
|
||||
"oxlint",
|
||||
].includes(addon),
|
||||
);
|
||||
if (validAddons.length > 0) {
|
||||
flags.push(`--addons ${validAddons.join(" ")}`);
|
||||
} else {
|
||||
if (DEFAULT_STACK.addons.length > 0) {
|
||||
flags.push("--addons none");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (DEFAULT_STACK.addons.length > 0) {
|
||||
flags.push("--addons none");
|
||||
@@ -1687,7 +1736,10 @@ const StackBuilder = () => {
|
||||
<TechIcon
|
||||
icon={tech.icon}
|
||||
name={tech.name}
|
||||
className="mr-1.5 h-3 w-3 sm:h-4 sm:w-4"
|
||||
className={cn(
|
||||
"mr-1.5 h-3 w-3 sm:h-4 sm:w-4",
|
||||
tech.className,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
|
||||
@@ -414,14 +414,14 @@ export default function AnalyticsPage() {
|
||||
|
||||
const loadAnalyticsData = useCallback(async () => {
|
||||
try {
|
||||
const response = await fetch("/analytics-data.json");
|
||||
const response = await fetch("https://r2.amanv.dev/analytics-data.json");
|
||||
const analyticsData = await response.json();
|
||||
|
||||
setData(analyticsData.data || []);
|
||||
setLastUpdated(analyticsData.lastUpdated || null);
|
||||
|
||||
console.log(
|
||||
`Loaded ${analyticsData.data?.length || 0} records from static JSON`,
|
||||
`Loaded ${analyticsData.data?.length || 0} records from R2 bucket`,
|
||||
);
|
||||
console.log(`Data generated at: ${analyticsData.generatedAt}`);
|
||||
} catch (error: unknown) {
|
||||
|
||||
11
apps/web/src/app/llms-full.txt/route.ts
Normal file
11
apps/web/src/app/llms-full.txt/route.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { getLLMText } from "@/lib/get-llm-text";
|
||||
import { source } from "@/lib/source";
|
||||
|
||||
export const revalidate = false;
|
||||
|
||||
export async function GET() {
|
||||
const scan = source.getPages().map(getLLMText);
|
||||
const scanned = await Promise.all(scan);
|
||||
|
||||
return new Response(scanned.join("\n\n"));
|
||||
}
|
||||
21
apps/web/src/app/llms.mdx/[[...slug]]/route.ts
Normal file
21
apps/web/src/app/llms.mdx/[[...slug]]/route.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { getLLMText } from "@/lib/get-llm-text";
|
||||
import { source } from "@/lib/source";
|
||||
|
||||
export const revalidate = false;
|
||||
|
||||
export async function GET(
|
||||
_req: NextRequest,
|
||||
{ params }: { params: Promise<{ slug?: string[] }> },
|
||||
) {
|
||||
const { slug } = await params;
|
||||
const page = source.getPage(slug);
|
||||
if (!page) notFound();
|
||||
|
||||
return new NextResponse(await getLLMText(page));
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
return source.generateParams();
|
||||
}
|
||||
@@ -1,4 +1,17 @@
|
||||
export const TECH_OPTIONS = {
|
||||
import type { TechCategory } from "./types";
|
||||
|
||||
export const TECH_OPTIONS: Record<
|
||||
TechCategory,
|
||||
{
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
default?: boolean;
|
||||
className?: string;
|
||||
}[]
|
||||
> = {
|
||||
api: [
|
||||
{
|
||||
id: "trpc",
|
||||
@@ -97,6 +110,7 @@ export const TECH_OPTIONS = {
|
||||
description: "Expo with NativeWind (Tailwind)",
|
||||
icon: "/icon/expo.svg",
|
||||
color: "from-purple-400 to-purple-600",
|
||||
className: "invert-0 dark:invert",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
@@ -105,6 +119,7 @@ export const TECH_OPTIONS = {
|
||||
description: "Expo with Unistyles",
|
||||
icon: "/icon/expo.svg",
|
||||
color: "from-pink-400 to-pink-600",
|
||||
className: "invert-0 dark:invert",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
@@ -368,6 +383,7 @@ export const TECH_OPTIONS = {
|
||||
description: "Default package manager",
|
||||
icon: "/icon/npm.svg",
|
||||
color: "from-red-500 to-red-700",
|
||||
className: "invert-0 dark:invert",
|
||||
},
|
||||
{
|
||||
id: "pnpm",
|
||||
@@ -388,8 +404,8 @@ export const TECH_OPTIONS = {
|
||||
addons: [
|
||||
{
|
||||
id: "pwa",
|
||||
name: "PWA",
|
||||
description: "Progressive Web App",
|
||||
name: "PWA (Progressive Web App)",
|
||||
description: "Make your app installable and work offline",
|
||||
icon: "",
|
||||
color: "from-blue-500 to-blue-700",
|
||||
default: false,
|
||||
@@ -397,7 +413,7 @@ export const TECH_OPTIONS = {
|
||||
{
|
||||
id: "tauri",
|
||||
name: "Tauri",
|
||||
description: "Desktop app support",
|
||||
description: "Build native desktop apps",
|
||||
icon: "/icon/tauri.svg",
|
||||
color: "from-amber-500 to-amber-700",
|
||||
default: false,
|
||||
@@ -405,7 +421,7 @@ export const TECH_OPTIONS = {
|
||||
{
|
||||
id: "starlight",
|
||||
name: "Starlight",
|
||||
description: "Documentation site with Astro",
|
||||
description: "Build stellar docs with astro",
|
||||
icon: "/icon/starlight.svg",
|
||||
color: "from-teal-500 to-teal-700",
|
||||
default: false,
|
||||
@@ -413,7 +429,7 @@ export const TECH_OPTIONS = {
|
||||
{
|
||||
id: "biome",
|
||||
name: "Biome",
|
||||
description: "Linting & formatting",
|
||||
description: "Format, lint, and more",
|
||||
icon: "/icon/biome.svg",
|
||||
color: "from-green-500 to-green-700",
|
||||
default: false,
|
||||
@@ -421,15 +437,40 @@ export const TECH_OPTIONS = {
|
||||
{
|
||||
id: "husky",
|
||||
name: "Husky",
|
||||
description: "Git hooks & lint-staged",
|
||||
description: "Modern native Git hooks made easy",
|
||||
icon: "",
|
||||
color: "from-purple-500 to-purple-700",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "ultracite",
|
||||
name: "Ultracite",
|
||||
description: "Biome preset with AI integration",
|
||||
icon: "/icon/ultracite.svg",
|
||||
color: "from-blue-500 to-blue-700",
|
||||
className: "invert-0 dark:invert",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "fumadocs",
|
||||
name: "Fumadocs",
|
||||
description: "Build excellent documentation site",
|
||||
icon: "",
|
||||
color: "from-indigo-500 to-indigo-700",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "oxlint",
|
||||
name: "Oxlint",
|
||||
description: "Rust-powered linter",
|
||||
icon: "",
|
||||
color: "from-orange-500 to-orange-700",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "turborepo",
|
||||
name: "Turborepo",
|
||||
description: "Monorepo build system",
|
||||
description: "High-performance build system",
|
||||
icon: "/icon/turborepo.svg",
|
||||
color: "from-gray-400 to-gray-700",
|
||||
default: true,
|
||||
|
||||
26
apps/web/src/lib/get-llm-text.ts
Normal file
26
apps/web/src/lib/get-llm-text.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { InferPageType } from "fumadocs-core/source";
|
||||
import { remarkInclude } from "fumadocs-mdx/config";
|
||||
import { remark } from "remark";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMdx from "remark-mdx";
|
||||
import type { source } from "@/lib/source";
|
||||
|
||||
const processor = remark()
|
||||
.use(remarkMdx)
|
||||
// needed for Fumadocs MDX
|
||||
.use(remarkInclude)
|
||||
.use(remarkGfm);
|
||||
|
||||
export async function getLLMText(page: InferPageType<typeof source>) {
|
||||
const processed = await processor.process({
|
||||
path: page.data._file.absolutePath,
|
||||
value: page.data.content,
|
||||
});
|
||||
|
||||
return `# ${page.data.title}
|
||||
URL: ${page.url}
|
||||
|
||||
${page.data.description}
|
||||
|
||||
${processed.value}`;
|
||||
}
|
||||
@@ -1,27 +1,19 @@
|
||||
export type TechCategory =
|
||||
| "core"
|
||||
| "frontend"
|
||||
| "api"
|
||||
| "webFrontend"
|
||||
| "nativeFrontend"
|
||||
| "runtime"
|
||||
| "backend"
|
||||
| "database"
|
||||
| "auth"
|
||||
| "orm"
|
||||
| "router";
|
||||
|
||||
export interface TechNode {
|
||||
id: string;
|
||||
type: string;
|
||||
position: { x: number; y: number };
|
||||
data: {
|
||||
label: string;
|
||||
category: TechCategory;
|
||||
description: string;
|
||||
isDefault: boolean;
|
||||
alternatives?: string[];
|
||||
isActive: boolean;
|
||||
group?: TechCategory;
|
||||
isStatic?: boolean;
|
||||
};
|
||||
}
|
||||
| "dbSetup"
|
||||
| "webDeploy"
|
||||
| "auth"
|
||||
| "packageManager"
|
||||
| "addons"
|
||||
| "examples"
|
||||
| "git"
|
||||
| "install";
|
||||
|
||||
export interface TechEdge {
|
||||
id: string;
|
||||
|
||||
Reference in New Issue
Block a user