@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
1,365 lines (1,225 loc) โข 38.9 kB
JavaScript
"use strict";
/**
* Aleph.js Framework Template Generator
* React SSR/SSG framework for Deno
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AlephGenerator = void 0;
const deno_base_generator_1 = require("./deno-base-generator");
const fs_1 = require("fs");
const path = __importStar(require("path"));
class AlephGenerator extends deno_base_generator_1.DenoBackendGenerator {
constructor() {
super('Aleph.js');
}
async generateFrameworkFiles(projectPath, options) {
// Generate Aleph-specific configuration
await this.generateAlephConfig(projectPath, options);
// Generate main app component
await this.generateApp(projectPath);
// Generate pages
await this.generatePages(projectPath);
// Generate API routes
await this.generateAPIRoutes(projectPath);
// Generate components
await this.generateComponents(projectPath);
// Generate layouts
await this.generateLayouts(projectPath);
// Generate styles
await this.generateStyles(projectPath);
// Generate utilities
await this.generateUtilities(projectPath);
// Generate static assets
await this.generateStaticAssets(projectPath);
// Update Aleph-specific configurations
await this.updateAlephConfig(projectPath, options);
}
async generateAlephConfig(projectPath, options) {
// Generate aleph.config.ts
const alephConfig = `import type { Config } from "aleph/types";
export default <Config>{
ssr: true,
build: {
target: "es2020",
browsers: {
chrome: 95,
firefox: 90,
safari: 14,
edge: 95,
},
},
server: {
port: ${options.port || 8000},
headers: {
"X-Frame-Options": "DENY",
"X-Content-Type-Options": "nosniff",
"X-XSS-Protection": "1; mode=block",
},
},
plugins: [],
env: async () => {
const env = await import("./env.ts");
return env.default;
},
};
`;
await fs_1.promises.writeFile(path.join(projectPath, 'aleph.config.ts'), alephConfig);
// Generate env.ts
const envContent = `export default {
API_URL: Deno.env.get("API_URL") || "http://localhost:8000/api",
SITE_TITLE: Deno.env.get("SITE_TITLE") || "${options.name}",
GA_TRACKING_ID: Deno.env.get("GA_TRACKING_ID") || "",
SENTRY_DSN: Deno.env.get("SENTRY_DSN") || "",
};
`;
await fs_1.promises.writeFile(path.join(projectPath, 'env.ts'), envContent);
// Update deno.json for Aleph
const denoJsonPath = path.join(projectPath, 'deno.json');
const denoConfig = JSON.parse(await fs_1.promises.readFile(denoJsonPath, 'utf-8'));
denoConfig.imports = {
...denoConfig.imports,
"aleph/": "https://deno.land/x/aleph@1.0.0-rc.1/",
"aleph/types": "https://deno.land/x/aleph@1.0.0-rc.1/types.d.ts",
"react": "https://esm.sh/react@18.2.0",
"react-dom": "https://esm.sh/react-dom@18.2.0",
"react/jsx-runtime": "https://esm.sh/react@18.2.0/jsx-runtime",
"@unocss/reset": "https://esm.sh/@unocss/reset@0.58.0/tailwind.css",
"unocss": "https://esm.sh/@unocss/core@0.58.0",
"swr": "https://esm.sh/swr@2.2.4",
"zustand": "https://esm.sh/zustand@4.4.7",
};
denoConfig.tasks = {
...denoConfig.tasks,
"dev": "deno run -A dev.ts",
"start": "deno run -A server.ts",
"build": "deno run -A build.ts",
"preview": "deno run -A preview.ts",
};
denoConfig.compilerOptions = {
...denoConfig.compilerOptions,
"jsx": "react-jsx",
"jsxImportSource": "react",
};
await fs_1.promises.writeFile(denoJsonPath, JSON.stringify(denoConfig, null, 2));
}
async generateApp(projectPath) {
// Generate dev.ts
const devContent = `import { serve } from "aleph/server";
import routes from "./routes.gen.ts";
serve({
routes,
baseUrl: import.meta.url,
port: Deno.env.get("PORT") ? Number(Deno.env.get("PORT")) : 8000,
});
`;
await fs_1.promises.writeFile(path.join(projectPath, 'dev.ts'), devContent);
// Generate server.ts
const serverContent = `import { serve } from "aleph/server";
import routes from "./routes.gen.ts";
const port = Deno.env.get("PORT") ? Number(Deno.env.get("PORT")) : 8000;
serve({
routes,
baseUrl: import.meta.url,
port,
cert: Deno.env.get("CERT_FILE"),
key: Deno.env.get("KEY_FILE"),
});
console.log(\`Server running on http://localhost:\${port}\`);
`;
await fs_1.promises.writeFile(path.join(projectPath, 'server.ts'), serverContent);
// Generate build.ts
const buildContent = `import { build } from "aleph/build";
await build({
baseUrl: import.meta.url,
outputDir: "./dist",
});
`;
await fs_1.promises.writeFile(path.join(projectPath, 'build.ts'), buildContent);
// Generate app.tsx
const appContent = `import React, { FC } from "react";
import "@unocss/reset";
import "./styles/global.css";
export const App: FC<{ Page: FC; pageProps: any }> = ({ Page, pageProps }) => {
return (
<>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
</head>
<Page {...pageProps} />
</>
);
};
export default App;
`;
await fs_1.promises.writeFile(path.join(projectPath, 'app.tsx'), appContent);
}
async generatePages(projectPath) {
const pagesDir = path.join(projectPath, 'pages');
await fs_1.promises.mkdir(pagesDir, { recursive: true });
// Index page
const indexPageContent = `import React, { FC } from "react";
import { Head } from "aleph/react";
import { Header } from "../components/Header.tsx";
import { Hero } from "../components/Hero.tsx";
import { Features } from "../components/Features.tsx";
export const Page: FC = () => {
return (
<>
<Head>
<title>Welcome to Aleph.js</title>
<meta name="description" content="A React SSR/SSG framework for Deno" />
</Head>
<div className="min-h-screen bg-gray-50">
<Header />
<main>
<Hero />
<Features />
</main>
</div>
</>
);
};
export default Page;
`;
await fs_1.promises.writeFile(path.join(pagesDir, 'index.tsx'), indexPageContent);
// About page
const aboutPageContent = `import React, { FC } from "react";
import { Head } from "aleph/react";
import { Header } from "../components/Header.tsx";
export const Page: FC = () => {
return (
<>
<Head>
<title>About - Aleph.js App</title>
</Head>
<div className="min-h-screen bg-gray-50">
<Header />
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<h1 className="text-4xl font-bold text-gray-900 mb-8">About</h1>
<div className="prose prose-lg">
<p>
This is an Aleph.js application built with React and Deno.
Aleph.js is a full-stack framework that brings the best
developer experience for building web applications.
</p>
<h2>Features</h2>
<ul>
<li>Server-side rendering (SSR)</li>
<li>Static site generation (SSG)</li>
<li>File-based routing</li>
<li>Built-in CSS support</li>
<li>TypeScript by default</li>
<li>Hot module replacement</li>
<li>Optimized production builds</li>
</ul>
</div>
</main>
</div>
</>
);
};
export default Page;
`;
await fs_1.promises.writeFile(path.join(pagesDir, 'about.tsx'), aboutPageContent);
// Blog index page
const blogDir = path.join(pagesDir, 'blog');
await fs_1.promises.mkdir(blogDir, { recursive: true });
const blogIndexContent = `import React, { FC } from "react";
import { Head } from "aleph/react";
import { Header } from "../../components/Header.tsx";
import { useData } from "aleph/react";
import { BlogPost } from "../../types/blog.ts";
export const data = async () => {
// In production, fetch from API or database
const posts: BlogPost[] = [
{
id: "1",
slug: "hello-world",
title: "Hello World",
excerpt: "Welcome to our blog built with Aleph.js",
date: "2024-01-01",
author: "John Doe",
},
{
id: "2",
slug: "getting-started",
title: "Getting Started with Aleph.js",
excerpt: "Learn how to build amazing apps with Aleph.js",
date: "2024-01-02",
author: "Jane Smith",
},
];
return { posts };
};
export const Page: FC = () => {
const { posts } = useData<{ posts: BlogPost[] }>();
return (
<>
<Head>
<title>Blog - Aleph.js App</title>
</Head>
<div className="min-h-screen bg-gray-50">
<Header />
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<h1 className="text-4xl font-bold text-gray-900 mb-8">Blog</h1>
<div className="grid gap-6">
{posts.map((post) => (
<article
key={post.id}
className="bg-white rounded-lg shadow p-6 hover:shadow-lg transition-shadow"
>
<h2 className="text-2xl font-semibold mb-2">
<a
href={\`/blog/\${post.slug}\`}
className="text-gray-900 hover:text-blue-600"
>
{post.title}
</a>
</h2>
<p className="text-gray-600 mb-4">{post.excerpt}</p>
<div className="text-sm text-gray-500">
By {post.author} on {post.date}
</div>
</article>
))}
</div>
</main>
</div>
</>
);
};
export default Page;
`;
await fs_1.promises.writeFile(path.join(blogDir, 'index.tsx'), blogIndexContent);
// Dynamic blog post page
const blogPostContent = `import React, { FC } from "react";
import { Head } from "aleph/react";
import { Header } from "../../components/Header.tsx";
import { useData, useRouter } from "aleph/react";
import { BlogPost } from "../../types/blog.ts";
export const data = async ({ params }: { params: Record<string, string> }) => {
const { slug } = params;
// In production, fetch from API or database
const post: BlogPost = {
id: "1",
slug,
title: "Hello World",
excerpt: "Welcome to our blog",
content: "This is the full blog post content. In a real application, this would be fetched from a database or CMS.",
date: "2024-01-01",
author: "John Doe",
};
return { post };
};
export const Page: FC = () => {
const { post } = useData<{ post: BlogPost }>();
return (
<>
<Head>
<title>{post.title} - Blog</title>
<meta name="description" content={post.excerpt} />
</Head>
<div className="min-h-screen bg-gray-50">
<Header />
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<article className="bg-white rounded-lg shadow p-8">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
{post.title}
</h1>
<div className="text-sm text-gray-500 mb-8">
By {post.author} on {post.date}
</div>
<div className="prose prose-lg max-w-none">
{post.content}
</div>
</article>
</main>
</div>
</>
);
};
export default Page;
`;
await fs_1.promises.writeFile(path.join(blogDir, '[slug].tsx'), blogPostContent);
// 404 page
const notFoundContent = `import React, { FC } from "react";
import { Head } from "aleph/react";
export const Page: FC = () => {
return (
<>
<Head>
<title>404 - Page Not Found</title>
</Head>
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<h1 className="text-6xl font-bold text-gray-900 mb-4">404</h1>
<p className="text-xl text-gray-600 mb-8">Page not found</p>
<a
href="/"
className="inline-block px-6 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-700"
>
Go Home
</a>
</div>
</div>
</>
);
};
export default Page;
`;
await fs_1.promises.writeFile(path.join(pagesDir, '404.tsx'), notFoundContent);
}
async generateAPIRoutes(projectPath) {
const apiDir = path.join(projectPath, 'api');
await fs_1.promises.mkdir(apiDir, { recursive: true });
// Health check API
const healthAPIContent = `export default {
GET: () => {
return Response.json({
status: "healthy",
timestamp: new Date().toISOString(),
service: "aleph-api",
version: "1.0.0",
});
},
};
`;
await fs_1.promises.writeFile(path.join(apiDir, 'health.ts'), healthAPIContent);
// Users API
const usersAPIContent = `import { z } from "https://deno.land/x/zod@v3.22.4/mod.ts";
import { getUsers, getUserById, createUser, updateUser, deleteUser } from "../utils/db.ts";
const UserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
export default {
async GET(req: Request) {
const url = new URL(req.url);
const id = url.searchParams.get("id");
if (id) {
const user = await getUserById(id);
if (!user) {
return new Response("User not found", { status: 404 });
}
return Response.json(user);
}
const page = parseInt(url.searchParams.get("page") || "1");
const limit = parseInt(url.searchParams.get("limit") || "10");
const users = await getUsers({ page, limit });
return Response.json(users);
},
async POST(req: Request) {
try {
const data = await req.json();
const validatedData = UserSchema.parse(data);
const user = await createUser(validatedData);
return Response.json(user, { status: 201 });
} catch (error) {
if (error instanceof z.ZodError) {
return Response.json(
{ error: "Validation failed", details: error.errors },
{ status: 400 }
);
}
return Response.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
},
async PUT(req: Request) {
const url = new URL(req.url);
const id = url.searchParams.get("id");
if (!id) {
return new Response("User ID required", { status: 400 });
}
try {
const data = await req.json();
const validatedData = UserSchema.partial().parse(data);
const user = await updateUser(id, validatedData);
if (!user) {
return new Response("User not found", { status: 404 });
}
return Response.json(user);
} catch (error) {
if (error instanceof z.ZodError) {
return Response.json(
{ error: "Validation failed", details: error.errors },
{ status: 400 }
);
}
return Response.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
},
async DELETE(req: Request) {
const url = new URL(req.url);
const id = url.searchParams.get("id");
if (!id) {
return new Response("User ID required", { status: 400 });
}
const deleted = await deleteUser(id);
if (!deleted) {
return new Response("User not found", { status: 404 });
}
return new Response(null, { status: 204 });
},
};
`;
await fs_1.promises.writeFile(path.join(apiDir, 'users.ts'), usersAPIContent);
// Auth API
const authAPIContent = `import { z } from "https://deno.land/x/zod@v3.22.4/mod.ts";
import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
import * as djwt from "https://deno.land/x/djwt@v3.0.1/mod.ts";
const LoginSchema = z.object({
email: z.string().email(),
password: z.string().min(6),
});
const RegisterSchema = LoginSchema.extend({
name: z.string().min(1),
});
const JWT_SECRET = Deno.env.get("JWT_SECRET") || "your-secret-key";
export default {
async POST(req: Request) {
const url = new URL(req.url);
const isLogin = url.pathname.endsWith("/login");
try {
const data = await req.json();
if (isLogin) {
const { email, password } = LoginSchema.parse(data);
// In production, verify against database
const hashedPassword = await bcrypt.hash(password);
const passwordMatch = await bcrypt.compare(password, hashedPassword);
if (!passwordMatch) {
return Response.json(
{ error: "Invalid credentials" },
{ status: 401 }
);
}
const token = await djwt.create(
{ alg: "HS256", typ: "JWT" },
{
email,
exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 7), // 7 days
},
JWT_SECRET
);
return Response.json({
token,
user: { email },
});
} else {
const { email, password, name } = RegisterSchema.parse(data);
// In production, save to database
const hashedPassword = await bcrypt.hash(password);
const token = await djwt.create(
{ alg: "HS256", typ: "JWT" },
{
email,
name,
exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 7),
},
JWT_SECRET
);
return Response.json({
token,
user: { email, name },
}, { status: 201 });
}
} catch (error) {
if (error instanceof z.ZodError) {
return Response.json(
{ error: "Validation failed", details: error.errors },
{ status: 400 }
);
}
return Response.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
},
};
`;
await fs_1.promises.writeFile(path.join(apiDir, 'auth/[action].ts'), authAPIContent);
}
async generateComponents(projectPath) {
const componentsDir = path.join(projectPath, 'components');
await fs_1.promises.mkdir(componentsDir, { recursive: true });
// Header component
const headerContent = `import React, { FC } from "react";
export const Header: FC = () => {
return (
<header className="bg-white shadow">
<nav className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex">
<div className="flex-shrink-0 flex items-center">
<h1 className="text-xl font-bold">Aleph.js App</h1>
</div>
<div className="hidden sm:ml-6 sm:flex sm:space-x-8">
<a
href="/"
className="border-blue-500 text-gray-900 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium"
>
Home
</a>
<a
href="/about"
className="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium"
>
About
</a>
<a
href="/blog"
className="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium"
>
Blog
</a>
</div>
</div>
</div>
</nav>
</header>
);
};
`;
await fs_1.promises.writeFile(path.join(componentsDir, 'Header.tsx'), headerContent);
// Hero component
const heroContent = `import React, { FC } from "react";
export const Hero: FC = () => {
return (
<section className="bg-gradient-to-r from-blue-500 to-purple-600 text-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24 text-center">
<h1 className="text-5xl font-bold mb-6">
Welcome to Aleph.js
</h1>
<p className="text-xl mb-8 max-w-2xl mx-auto">
The React framework for building fast, modern web applications
with Deno. Server-side rendering, static generation, and more.
</p>
<div className="space-x-4">
<a
href="/docs"
className="inline-block px-8 py-3 bg-white text-blue-600 rounded-md font-semibold hover:bg-gray-100"
>
Get Started
</a>
<a
href="https://github.com"
className="inline-block px-8 py-3 bg-transparent border-2 border-white rounded-md font-semibold hover:bg-white hover:text-blue-600"
>
View on GitHub
</a>
</div>
</div>
</section>
);
};
`;
await fs_1.promises.writeFile(path.join(componentsDir, 'Hero.tsx'), heroContent);
// Features component
const featuresContent = `import React, { FC } from "react";
interface Feature {
title: string;
description: string;
icon: string;
}
const features: Feature[] = [
{
title: "Server-Side Rendering",
description: "Pre-render pages on the server for better SEO and performance",
icon: "๐",
},
{
title: "Static Site Generation",
description: "Generate static pages at build time for lightning-fast loads",
icon: "โก",
},
{
title: "File-Based Routing",
description: "Create pages by adding files to the pages directory",
icon: "๐",
},
{
title: "TypeScript First",
description: "Built with TypeScript for type safety and better DX",
icon: "๐",
},
{
title: "Hot Module Replacement",
description: "See changes instantly without losing application state",
icon: "๐ฅ",
},
{
title: "Optimized Builds",
description: "Automatic code splitting and optimization for production",
icon: "๐ฆ",
},
];
export const Features: FC = () => {
return (
<section className="py-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-gray-900 mb-4">
Everything You Need
</h2>
<p className="text-xl text-gray-600 max-w-2xl mx-auto">
Aleph.js provides all the tools you need to build modern web applications
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{features.map((feature) => (
<div
key={feature.title}
className="bg-white p-6 rounded-lg shadow hover:shadow-lg transition-shadow"
>
<div className="text-4xl mb-4">{feature.icon}</div>
<h3 className="text-xl font-semibold mb-2">{feature.title}</h3>
<p className="text-gray-600">{feature.description}</p>
</div>
))}
</div>
</div>
</section>
);
};
`;
await fs_1.promises.writeFile(path.join(componentsDir, 'Features.tsx'), featuresContent);
// Button component
const buttonContent = `import React, { FC, ButtonHTMLAttributes } from "react";
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary" | "outline";
size?: "sm" | "md" | "lg";
}
export const Button: FC<ButtonProps> = ({
children,
variant = "primary",
size = "md",
className = "",
...props
}) => {
const baseStyles = "font-medium rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2";
const variants = {
primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
secondary: "bg-gray-600 text-white hover:bg-gray-700 focus:ring-gray-500",
outline: "bg-transparent border-2 border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-gray-500",
};
const sizes = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg",
};
return (
<button
className={\`\${baseStyles} \${variants[variant]} \${sizes[size]} \${className}\`}
{...props}
>
{children}
</button>
);
};
`;
await fs_1.promises.writeFile(path.join(componentsDir, 'Button.tsx'), buttonContent);
}
async generateLayouts(projectPath) {
const layoutsDir = path.join(projectPath, 'layouts');
await fs_1.promises.mkdir(layoutsDir, { recursive: true });
// Default layout
const defaultLayoutContent = `import React, { FC, ReactNode } from "react";
import { Header } from "../components/Header.tsx";
import { Footer } from "../components/Footer.tsx";
interface DefaultLayoutProps {
children: ReactNode;
}
export const DefaultLayout: FC<DefaultLayoutProps> = ({ children }) => {
return (
<div className="min-h-screen flex flex-col">
<Header />
<main className="flex-1">
{children}
</main>
<Footer />
</div>
);
};
`;
await fs_1.promises.writeFile(path.join(layoutsDir, 'DefaultLayout.tsx'), defaultLayoutContent);
// Create Footer component
const footerContent = `import React, { FC } from "react";
export const Footer: FC = () => {
return (
<footer className="bg-gray-900 text-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex justify-between items-center">
<p>© 2024 Aleph.js App. All rights reserved.</p>
<div className="flex space-x-6">
<a href="/privacy" className="hover:text-gray-300">Privacy</a>
<a href="/terms" className="hover:text-gray-300">Terms</a>
<a href="/contact" className="hover:text-gray-300">Contact</a>
</div>
</div>
</div>
</footer>
);
};
`;
const componentsDir = path.join(projectPath, 'components');
await fs_1.promises.mkdir(componentsDir, { recursive: true });
await fs_1.promises.writeFile(path.join(componentsDir, 'Footer.tsx'), footerContent);
}
async generateStyles(projectPath) {
const stylesDir = path.join(projectPath, 'styles');
await fs_1.promises.mkdir(stylesDir, { recursive: true });
// Global styles
const globalStyles = `/* Global Styles */
:root {
--color-primary: #3b82f6;
--color-primary-dark: #2563eb;
--color-secondary: #6b7280;
--color-success: #10b981;
--color-danger: #ef4444;
--color-warning: #f59e0b;
}
* {
box-sizing: border-box;
}
html {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
padding: 0;
background-color: #f9fafb;
color: #111827;
}
/* Utility Classes */
.container {
max-width: 1280px;
margin: 0 auto;
padding: 0 1rem;
}
/* Animation Classes */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fadeIn 0.5s ease-out;
}
/* Loading Spinner */
.spinner {
border: 2px solid #f3f4f6;
border-top: 2px solid var(--color-primary);
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Prose Styles for Content */
.prose {
color: #374151;
max-width: 65ch;
}
.prose h1 {
font-size: 2.25rem;
font-weight: 800;
line-height: 2.5rem;
margin-bottom: 2rem;
}
.prose h2 {
font-size: 1.875rem;
font-weight: 700;
line-height: 2.25rem;
margin-top: 3rem;
margin-bottom: 1.5rem;
}
.prose p {
margin-bottom: 1.25rem;
line-height: 1.75;
}
.prose ul,
.prose ol {
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.prose li {
margin-bottom: 0.5rem;
}
.prose a {
color: var(--color-primary);
text-decoration: underline;
}
.prose a:hover {
color: var(--color-primary-dark);
}
/* Form Styles */
.form-input {
display: block;
width: 100%;
padding: 0.5rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #374151;
background-color: #fff;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
/* Responsive Grid */
@media (min-width: 640px) {
.sm\\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 768px) {
.md\\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
@media (min-width: 1024px) {
.lg\\:grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
`;
await fs_1.promises.writeFile(path.join(stylesDir, 'global.css'), globalStyles);
// UnoCSS config
const unoConfig = `import { defineConfig, presetUno, presetTypography } from "unocss";
export default defineConfig({
presets: [
presetUno(),
presetTypography(),
],
theme: {
colors: {
primary: {
DEFAULT: '#3b82f6',
dark: '#2563eb',
light: '#60a5fa',
},
},
},
});
`;
await fs_1.promises.writeFile(path.join(projectPath, 'uno.config.ts'), unoConfig);
}
async generateUtilities(projectPath) {
const utilsDir = path.join(projectPath, 'utils');
await fs_1.promises.mkdir(utilsDir, { recursive: true });
// Database utilities
const dbContent = `// Mock database utilities
// Replace with actual database integration
interface User {
id: string;
name: string;
email: string;
createdAt: Date;
updatedAt: Date;
}
const users: Map<string, User> = new Map();
export async function getUserById(id: string): Promise<User | null> {
return users.get(id) || null;
}
export async function getUsers(params: { page: number; limit: number }) {
const allUsers = Array.from(users.values());
const start = (params.page - 1) * params.limit;
const end = start + params.limit;
return {
users: allUsers.slice(start, end),
total: allUsers.length,
page: params.page,
limit: params.limit,
};
}
export async function createUser(data: Omit<User, "id" | "createdAt" | "updatedAt">): Promise<User> {
const user: User = {
...data,
id: crypto.randomUUID(),
createdAt: new Date(),
updatedAt: new Date(),
};
users.set(user.id, user);
return user;
}
export async function updateUser(id: string, data: Partial<Omit<User, "id" | "createdAt">>): Promise<User | null> {
const user = users.get(id);
if (!user) return null;
const updated = {
...user,
...data,
updatedAt: new Date(),
};
users.set(id, updated);
return updated;
}
export async function deleteUser(id: string): Promise<boolean> {
return users.delete(id);
}
`;
await fs_1.promises.writeFile(path.join(utilsDir, 'db.ts'), dbContent);
// API client utilities
const apiClientContent = `import { useSWR } from "swr";
const API_BASE = "/api";
async function fetcher(url: string) {
const res = await fetch(url);
if (!res.ok) {
const error = new Error("An error occurred while fetching the data.");
throw error;
}
return res.json();
}
export function useAPI<T = any>(endpoint: string) {
const { data, error, mutate } = useSWR<T>(\`\${API_BASE}\${endpoint}\`, fetcher);
return {
data,
isLoading: !error && !data,
isError: error,
mutate,
};
}
export async function apiRequest(endpoint: string, options?: RequestInit) {
const response = await fetch(\`\${API_BASE}\${endpoint}\`, {
headers: {
"Content-Type": "application/json",
...options?.headers,
},
...options,
});
if (!response.ok) {
throw new Error(\`API request failed: \${response.statusText}\`);
}
return response.json();
}
`;
await fs_1.promises.writeFile(path.join(utilsDir, 'api.ts'), apiClientContent);
// Validation utilities
const validationContent = `export function isValidEmail(email: string): boolean {
const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
return emailRegex.test(email);
}
export function isValidUrl(url: string): boolean {
try {
new URL(url);
return true;
} catch {
return false;
}
}
export function sanitizeHtml(html: string): string {
return html
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'")
.replace(/\\//g, "/");
}
export function formatDate(date: string | Date): string {
const d = new Date(date);
return d.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
}
`;
await fs_1.promises.writeFile(path.join(utilsDir, 'validation.ts'), validationContent);
}
async generateStaticAssets(projectPath) {
const publicDir = path.join(projectPath, 'public');
await fs_1.promises.mkdir(publicDir, { recursive: true });
// Create favicon
const faviconContent = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<rect width="100" height="100" fill="#3b82f6"/>
<text x="50" y="65" text-anchor="middle" font-family="Arial" font-size="50" font-weight="bold" fill="white">A</text>
</svg>`;
await fs_1.promises.writeFile(path.join(publicDir, 'favicon.svg'), faviconContent);
// Create robots.txt
const robotsContent = `User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
`;
await fs_1.promises.writeFile(path.join(publicDir, 'robots.txt'), robotsContent);
}
async generateTypes(projectPath) {
const typesDir = path.join(projectPath, 'types');
await fs_1.promises.mkdir(typesDir, { recursive: true });
// Blog types
const blogTypesContent = `export interface BlogPost {
id: string;
slug: string;
title: string;
excerpt: string;
content?: string;
author: string;
date: string;
tags?: string[];
featured?: boolean;
coverImage?: string;
}
export interface BlogCategory {
id: string;
name: string;
slug: string;
description?: string;
}
`;
await fs_1.promises.writeFile(path.join(typesDir, 'blog.ts'), blogTypesContent);
// API types
const apiTypesContent = `export interface APIResponse<T = any> {
data?: T;
error?: APIError;
meta?: APIMeta;
}
export interface APIError {
code: string;
message: string;
details?: any;
}
export interface APIMeta {
page?: number;
limit?: number;
total?: number;
hasMore?: boolean;
}
export interface User {
id: string;
email: string;
name: string;
avatar?: string;
createdAt: string;
updatedAt: string;
}
export interface AuthResponse {
token: string;
user: User;
}
`;
await fs_1.promises.writeFile(path.join(typesDir, 'api.ts'), apiTypesContent);
}
async updateAlephConfig(projectPath, options) {
// Update .gitignore
const gitignorePath = path.join(projectPath, '.gitignore');
const gitignoreContent = await fs_1.promises.readFile(gitignorePath, 'utf-8');
const alephIgnore = `
# Aleph.js
.aleph/
dist/
`;
await fs_1.promises.writeFile(gitignorePath, gitignoreContent + alephIgnore);
// Create routes.gen.ts placeholder
const routesGenContent = `// This file is auto-generated by Aleph.js
// Do not edit this file manually
export default [];
`;
await fs_1.promises.writeFile(path.join(projectPath, 'routes.gen.ts'), routesGenContent);
// Update README
const readmeContent = `# ${options.name}
## Aleph.js + React Application
### ๐ Built with Aleph.js
This project uses [Aleph.js](https://alephjs.org/), a full-stack React framework for Deno.
### ๐ Running
#### Development
\`\`\`bash
deno task dev
\`\`\`
The application will start at http://localhost:${options.port || 8000}
#### Production
\`\`\`bash
deno task build
deno task start
\`\`\`
### ๐ Deployment
#### Deno Deploy
1. Push your project to GitHub
2. Go to https://dash.deno.com
3. Create a new project and link your GitHub repository
4. Set entry point to \`server.ts\`
#### Docker
\`\`\`bash
docker build -t ${options.name} .
docker run -p ${options.port || 8000}:${options.port || 8000} ${options.name}
\`\`\`
### ๐ Project Structure
- \`pages/\` - File-based routing
- \`api/\` - API routes
- \`components/\` - React components
- \`layouts/\` - Layout components
- \`styles/\` - CSS files
- \`utils/\` - Utility functions
- \`types/\` - TypeScript types
- \`public/\` - Static assets
### ๐งช Testing
\`\`\`bash
deno task test
\`\`\`
### ๐จ Features
- Server-side rendering (SSR)
- Static site generation (SSG)
- File-based routing
- API routes
- TypeScript by default
- Hot module replacement
- Optimized production builds
---
For more information, see the [Aleph.js documentation](https://alephjs.org/docs).
`;
await fs_1.promises.writeFile(path.join(projectPath, 'README.md'), readmeContent);
}
}
exports.AlephGenerator = AlephGenerator;