Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import ora from 'ora';
import tiged from 'tiged';
import Handlebars from 'handlebars';
import { execSync } from 'child_process';
import { resolveDependencies } from './dependencies.js';

// Register custom Handlebars helpers
Handlebars.registerHelper('eq', function (a, b) {
return a === b;
});

// Helper function to recursively find all files in a directory
function getAllFiles(dirPath, arrayOfFiles = []) {
Expand Down Expand Up @@ -68,7 +74,7 @@ export async function generateProject(config) {
const allFiles = getAllFiles(projectPath);

for (const file of allFiles) {
if (file.match(/\.(tsx|ts|json|md|html|css)$/)) {
if (file.match(/\.(tsx|ts|json|md|html|css|mjs)$/)) {
let content = fs.readFileSync(file, 'utf-8');
if (content.includes('{{')) {
const template = Handlebars.compile(content);
Expand All @@ -83,7 +89,10 @@ export async function generateProject(config) {
const configFilePath = path.join(projectPath, 'opusify.config.json');
fs.writeFileSync(configFilePath, JSON.stringify(config, null, 2));

// 5. AUTOMATION PHASE: Install Dependencies
// 5. Resolve dynamic dependencies based on user choices
resolveDependencies(projectPath, config);

// 6. AUTOMATION PHASE: Install Dependencies
const installSpinner = ora('Installing dependencies (this might take a minute)...').start();
try {
execSync('npm install', { cwd: projectPath, stdio: 'pipe' });
Expand All @@ -92,7 +101,7 @@ export async function generateProject(config) {
installSpinner.fail('Could not install dependencies. You may need to run npm install manually.');
}

// 6. Git Initialization
// 7. Git Initialization
if (config.initGit) {
const gitSpinner = ora('Initializing Git repository...').start();
try {
Expand Down
26 changes: 22 additions & 4 deletions templates/ecommerce/nextjs-monolith/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { Metadata } from 'next';
import Link from 'next/link';
import './globals.css';
import AnimationProvider from '../components/AnimationProvider';
{{#if (eq design "Dark Terminal")}}
import { Terminal } from 'lucide-react';
{{/if}}

export const metadata: Metadata = {
title: '{{projectName}} - Store',
Expand All @@ -18,7 +22,10 @@ function Navbar() {
return (
<header className="border-b border-border bg-card">
<nav className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
<Link href="/" className="text-xl font-bold text-primary">
<Link href="/" className="text-xl font-bold text-primary flex items-center gap-2">
{{#if (eq design "Dark Terminal")}}
<Terminal className="w-5 h-5" />
{{/if}}
{{projectName}}
</Link>
<ul className="flex gap-6">
Expand All @@ -41,7 +48,10 @@ function Navbar() {
function Sidebar() {
return (
<aside className="w-64 border-r border-border bg-card min-h-screen p-6">
<Link href="/" className="text-xl font-bold text-primary block mb-8">
<Link href="/" className="text-xl font-bold text-primary flex items-center gap-2 mb-8">
{{#if (eq design "Dark Terminal")}}
<Terminal className="w-5 h-5" />
{{/if}}
{{projectName}}
</Link>
<nav>
Expand Down Expand Up @@ -75,12 +85,20 @@ export default function RootLayout({
{useSidebar ? (
<div className="flex">
<Sidebar />
<main className="flex-1">{children}</main>
<main className="flex-1">
<AnimationProvider>
{children}
</AnimationProvider>
</main>
</div>
) : (
<>
<Navbar />
<main>{children}</main>
<main>
<AnimationProvider>
{children}
</AnimationProvider>
</main>
</>
)}
</body>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{{#if (eq design "Glassmorphism")}}
'use client';

import { AnimatePresence, motion } from 'framer-motion';

export default function AnimationProvider({
children,
}: {
children: React.ReactNode;
}) {
return (
<AnimatePresence mode="wait">
<motion.div
initial=\{{ opacity: 0, y: 20 }}
animate=\{{ opacity: 1, y: 0 }}
exit=\{{ opacity: 0, y: -20 }}
transition=\{{ duration: 0.3 }}
>
{children}
</motion.div>
</AnimatePresence>
);
}
{{else}}
export default function AnimationProvider({
children,
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}
{{/if}}
16 changes: 15 additions & 1 deletion templates/portfolio/nextjs-monolith/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { Metadata } from 'next';
import './globals.css';
import AnimationProvider from '../components/AnimationProvider';
{{#if (eq design "Dark Terminal")}}
import { Terminal } from 'lucide-react';
{{/if}}

export const metadata: Metadata = {
title: '{{projectName}} - Portfolio',
Expand All @@ -13,7 +17,17 @@ export default function RootLayout({
}) {
return (
<html lang="en" data-theme="{{design}}">
<body>{children}</body>
<body>
{{#if (eq design "Dark Terminal")}}
<header className="border-b border-border bg-card px-6 py-3 flex items-center gap-2">
<Terminal className="w-5 h-5 text-primary" />
<span className="font-mono text-sm text-primary">{{projectName}}</span>
</header>
{{/if}}
<AnimationProvider>
{children}
</AnimationProvider>
</body>
</html>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{{#if (eq design "Glassmorphism")}}
'use client';

import { AnimatePresence, motion } from 'framer-motion';

export default function AnimationProvider({
children,
}: {
children: React.ReactNode;
}) {
return (
<AnimatePresence mode="wait">
<motion.div
initial=\{{ opacity: 0, y: 20 }}
animate=\{{ opacity: 1, y: 0 }}
exit=\{{ opacity: 0, y: -20 }}
transition=\{{ duration: 0.3 }}
>
{children}
</motion.div>
</AnimatePresence>
);
}
{{else}}
export default function AnimationProvider({
children,
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}
{{/if}}
9 changes: 9 additions & 0 deletions templates/portfolio/nextjs-monolith/env.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { z } from 'zod';

const envSchema = z.object({
DATABASE_URL: z.string().url().optional(),
NEXTAUTH_SECRET: z.string().min(32).optional(),
NEXTAUTH_URL: z.string().url().optional(),
});

export const env = envSchema.parse(process.env);
17 changes: 17 additions & 0 deletions templates/portfolio/nextjs-monolith/lib/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { env } from '../env.mjs';

/**
* Database connection using validated environment variables.
* The `env` object is validated at build time via Zod (see env.mjs).
* This ensures DATABASE_URL is always defined and correctly formatted.
*/

const globalForDb = globalThis as unknown as {
dbUrl: string | undefined;
};

export const databaseUrl = globalForDb.dbUrl ?? env.DATABASE_URL;

if (process.env.NODE_ENV !== 'production') {
globalForDb.dbUrl = databaseUrl;
}
Loading