diff --git a/.gitignore b/.gitignore index b512c09..f4f3f0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ -node_modules \ No newline at end of file +node_modules +test/generated-test-cases.js +debug-auth.js +quick-auth-test.js +test-auth-providers.js +verify-auth.js \ No newline at end of file diff --git a/README.md b/README.md index 30537d8..c8c0877 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ - **Conditional API Route Deletion** — deletes the default `api/hello.js` route if using the `src` directory and not the `app` directory. - **Safe Project Creation** — checks if the current directory is empty when creating a project in the current directory (`.`) and prevents accidental overwrites. - **ORM Support** — choose between no ORM, Prisma, and Drizzle. +- **Optional Auth Scaffolding** — supports NextAuth.js, Clerk, or Firebase - **Automated CI/CD Feedback** — Pull Requests now receive automated comments on test status. ## Installation @@ -58,6 +59,7 @@ When you run `npx create-next-quick` without a project name, you will be prompte 6. **Enter the names of the pages you want to create (default: none)** 7. **Choose a linter (default: none)** 8. **Choose an ORM (default: none)** +9. **Do you want to use Shadcn UI? (default: Yes)** Example run: @@ -76,13 +78,14 @@ npx create-next-quick ? Choose a linter (default: none): none ? Choose an ORM (default: none): prisma ? Do you want to use Shadcn UI? No +? Choose an authentication provider (default: none): nextauth ``` ## Commands -- `npm run dev` — starts the development server. -- `npm run build` — builds the project for production. -- `npm start` — starts the production server. +- `npm run dev` — starts the development server. +- `npm run build` — builds the project for production. +- `npm start` — starts the production server. ## Testing @@ -115,8 +118,8 @@ Our CI/CD pipeline will automatically run tests and provide feedback directly on Before submitting, please ensure: -- Your code follows project style guidelines -- You have tested your changes locally +- Your code follows project style guidelines +- You have tested your changes locally ## License @@ -128,4 +131,4 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file - \ No newline at end of file + diff --git a/index.js b/index.js index 94530de..6729cd3 100644 --- a/index.js +++ b/index.js @@ -1,36 +1,49 @@ #!/usr/bin/env node -import inquirer from "inquirer"; -import path from "path"; -import fs from "fs"; -import chalk from "chalk"; -import { run, deleteFolder, createFolder, deleteFile, fileExists, writeFile } from './lib/utils.js'; +import inquirer from 'inquirer'; +import path from 'path'; +import fs from 'fs'; +import chalk from 'chalk'; +import { + run, + deleteFolder, + createFolder, + deleteFile, + fileExists, + writeFile, +} from './lib/utils.js'; import { createPages, createLayout } from './lib/templates.js'; (async () => { - const availablePackageManagers = ["npm"]; + const availablePackageManagers = ['npm']; try { - run("yarn --version", process.cwd(), true); - availablePackageManagers.push("yarn"); - } catch (error) { } + run('yarn --version', process.cwd(), true); + availablePackageManagers.push('yarn'); + } catch (error) {} try { - run("pnpm --version", process.cwd(), true); - availablePackageManagers.push("pnpm"); - } catch (error) { } + run('pnpm --version', process.cwd(), true); + availablePackageManagers.push('pnpm'); + } catch (error) {} const validateProjectName = (input) => { if (input !== input.toLowerCase()) { - return chalk.red.bold("Project name must be in lowercase."); + return chalk.red.bold('Project name must be in lowercase.'); } - if (input === ".") { + if (input === '.') { const files = fs.readdirSync(process.cwd()); if (files.length > 0) { - return chalk.red.bold("The current directory is not empty. Please use a different project name."); + return chalk.red.bold( + 'The current directory is not empty. Please use a different project name.' + ); } } else { if (fs.existsSync(input)) { - return chalk.red.bold(`A directory named "${chalk.white(input)}" already exists. Please use a different project name.`); + return chalk.red.bold( + `A directory named "${chalk.white( + input + )}" already exists. Please use a different project name.` + ); } } return true; @@ -40,9 +53,13 @@ import { createPages, createLayout } from './lib/templates.js'; const answers = {}; console.log(); - console.log(chalk.bold.cyan("╔═══════════════════════════════════════════╗")); - console.log(chalk.bold.cyan("║") + chalk.bold.white(" 🚀 Create Next Quick CLI Tool ") + chalk.bold.cyan(" ║")); - console.log(chalk.bold.cyan("╚═══════════════════════════════════════════╝")); + console.log(chalk.bold.cyan('╔═══════════════════════════════════════════╗')); + console.log( + chalk.bold.cyan('║') + + chalk.bold.white(' 🚀 Create Next Quick CLI Tool ') + + chalk.bold.cyan(' ║') + ); + console.log(chalk.bold.cyan('╚═══════════════════════════════════════════╝')); console.log(); if (appName) { @@ -57,150 +74,189 @@ import { createPages, createLayout } from './lib/templates.js'; if (!answers.projectName) { const appNameAnswers = await inquirer.prompt([ { - type: "input", - name: "projectName", - message: "Enter project name:", - filter: (input) => input.trim() === '' ? '.' : input.trim(), - validate: validateProjectName - } + type: 'input', + name: 'projectName', + message: 'Enter project name:', + filter: (input) => (input.trim() === '' ? '.' : input.trim()), + validate: validateProjectName, + }, ]); answers.projectName = appNameAnswers.projectName; } const otherAnswers = await inquirer.prompt([ { - type: "list", - name: "packageManager", - message: "Choose a package manager:", + type: 'list', + name: 'packageManager', + message: 'Choose a package manager:', choices: availablePackageManagers, - default: "pnpm" + default: 'pnpm', }, { - type: "confirm", - name: "useTypeScript", - message: "Do you want to use TypeScript?", - default: true + type: 'confirm', + name: 'useTypeScript', + message: 'Do you want to use TypeScript?', + default: true, }, { - type: "confirm", - name: "useTailwind", - message: "Do you want to use Tailwind CSS?", - default: true + type: 'confirm', + name: 'useTailwind', + message: 'Do you want to use Tailwind CSS?', + default: true, }, { - type: "confirm", - name: "useSrcDir", - message: "Do you want to use src directory?", - default: true + type: 'confirm', + name: 'useSrcDir', + message: 'Do you want to use src directory?', + default: true, }, { - type: "confirm", - name: "useAppDir", - message: "Do you want to use the app directory?", - default: true + type: 'confirm', + name: 'useAppDir', + message: 'Do you want to use the app directory?', + default: true, }, { - type: "input", - name: "pages", - message: "Enter pages (comma-separated, default: none):", - default: "", - filter: (input) => input.split(',').map((page) => page.trim()).filter(page => page !== '') + type: 'input', + name: 'pages', + message: 'Enter pages (comma-separated, default: none):', + default: '', + filter: (input) => + input + .split(',') + .map((page) => page.trim()) + .filter((page) => page !== ''), }, { - type: "list", - name: "linter", - message: "Choose a linter:", - choices: ["none", "eslint", "biome"], - default: "none" + type: 'list', + name: 'linter', + message: 'Choose a linter:', + choices: ['none', 'eslint', 'biome'], + default: 'none', }, { - type: "list", - name: "orm", - message: "Choose an ORM:", - choices: ["none", "prisma", "drizzle"], - default: "none" + type: 'list', + name: 'orm', + message: 'Choose an ORM:', + choices: ['none', 'prisma', 'drizzle'], + default: 'none', }, { - type: "confirm", - name: "useShadcn", - message: "Do you want to use Shadcn UI?", - default: false - } + type: 'confirm', + name: 'useShadcn', + message: 'Do you want to use Shadcn UI?', + default: false, + }, + { + type: 'list', + name: 'auth', + message: 'Choose an authentication provider:', + choices: ['none', 'nextauth', 'clerk', 'firebase'], + default: 'none', + }, ]); Object.assign(answers, otherAnswers); - const { projectName, packageManager, useTypeScript, useTailwind, useAppDir, useSrcDir, pages, linter, orm, useShadcn } = answers; + const { + projectName, + packageManager, + useTypeScript, + useTailwind, + useAppDir, + useSrcDir, + pages, + linter, + orm, + useShadcn, + auth, + } = answers; const projectPath = path.join(process.cwd(), projectName); console.log(); - console.log(chalk.bold.hex("#23f0bcff")("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")); - console.log(chalk.bold.white(` Creating project: ${chalk.cyan(projectName)}`)); - console.log(chalk.bold.hex("#23f0bcff")("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")); + console.log( + chalk.bold.hex('#23f0bcff')('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━') + ); + console.log( + chalk.bold.white(` Creating project: ${chalk.cyan(projectName)}`) + ); + console.log( + chalk.bold.hex('#23f0bcff')('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━') + ); console.log(); let command = `npx --yes create-next-app@latest ${projectName} --use-${packageManager} --yes`; if (useTypeScript) { - command += " --ts"; + command += ' --ts'; } else { - command += " --js"; + command += ' --js'; } if (useTailwind) { - command += " --tailwind"; + command += ' --tailwind'; } if (useSrcDir) { - command += " --src-dir"; + command += ' --src-dir'; } if (useAppDir) { - command += " --app"; + command += ' --app'; } else { - command += " --no-app"; + command += ' --no-app'; } - if (linter === "none") { - command += " --no-eslint"; + if (linter === 'none') { + command += ' --no-eslint'; } - console.log(chalk.cyan(`Installing dependencies with ${chalk.bold(packageManager)}...`)); + console.log( + chalk.cyan(`Installing dependencies with ${chalk.bold(packageManager)}...`) + ); try { run(command); - console.log(chalk.bold.green("Dependencies installed successfully")); + console.log(chalk.bold.green('Dependencies installed successfully')); } catch (err) { - console.log(chalk.bold.red("Failed to install dependencies")); + console.log(chalk.bold.red('Failed to install dependencies')); process.exit(1); } - console.log(chalk.yellow("Cleaning up default files...")); + console.log(chalk.yellow('Cleaning up default files...')); if (!useAppDir) { const apiHelloPath = useSrcDir - ? path.join(projectPath, "src", "pages", "api", "hello.js") - : path.join(projectPath, "pages", "api", "hello.js"); + ? path.join(projectPath, 'src', 'pages', 'api', 'hello.js') + : path.join(projectPath, 'pages', 'api', 'hello.js'); if (fileExists(apiHelloPath)) { deleteFile(apiHelloPath); } } - const publicPath = path.join(projectPath, "public"); + const publicPath = path.join(projectPath, 'public'); deleteFolder(publicPath); createFolder(publicPath); - console.log(chalk.bold.green("Cleanup complete")); + console.log(chalk.bold.green('Cleanup complete')); - console.log(chalk.magenta("Creating layout files...")); + console.log(chalk.magenta('Creating layout files...')); createLayout(projectPath, projectName, useTypeScript, useAppDir, useSrcDir); const pagesPath = useAppDir - ? (useSrcDir ? path.join(projectPath, "src", "app") : path.join(projectPath, "app")) - : (useSrcDir ? path.join(projectPath, "src", "pages") : path.join(projectPath, "pages")); + ? useSrcDir + ? path.join(projectPath, 'src', 'app') + : path.join(projectPath, 'app') + : useSrcDir + ? path.join(projectPath, 'src', 'pages') + : path.join(projectPath, 'pages'); createPages(pagesPath, pages, useTypeScript, useAppDir, useSrcDir); const faviconPathInAppOrSrc = useAppDir - ? (useSrcDir ? path.join(projectPath, "src", "app", "favicon.ico") : path.join(projectPath, "app", "favicon.ico")) - : (useSrcDir ? path.join(projectPath, "src", "favicon.ico") : path.join(projectPath, "favicon.ico")); + ? useSrcDir + ? path.join(projectPath, 'src', 'app', 'favicon.ico') + : path.join(projectPath, 'app', 'favicon.ico') + : useSrcDir + ? path.join(projectPath, 'src', 'favicon.ico') + : path.join(projectPath, 'favicon.ico'); if (fileExists(faviconPathInAppOrSrc)) { deleteFile(faviconPathInAppOrSrc); @@ -209,12 +265,26 @@ import { createPages, createLayout } from './lib/templates.js'; let defaultPagePath; if (useAppDir) { defaultPagePath = useSrcDir - ? path.join(projectPath, "src", "app", useTypeScript ? "page.tsx" : "page.js") - : path.join(projectPath, "app", useTypeScript ? "page.tsx" : "page.js"); + ? path.join( + projectPath, + 'src', + 'app', + useTypeScript ? 'page.tsx' : 'page.js' + ) + : path.join(projectPath, 'app', useTypeScript ? 'page.tsx' : 'page.js'); } else { defaultPagePath = useSrcDir - ? path.join(projectPath, "src", "pages", useTypeScript ? "index.tsx" : "index.js") - : path.join(projectPath, "pages", useTypeScript ? "index.tsx" : "index.js"); + ? path.join( + projectPath, + 'src', + 'pages', + useTypeScript ? 'index.tsx' : 'index.js' + ) + : path.join( + projectPath, + 'pages', + useTypeScript ? 'index.tsx' : 'index.js' + ); } const emptyPageContent = `export default function Page() { @@ -225,28 +295,30 @@ import { createPages, createLayout } from './lib/templates.js'; writeFile(defaultPagePath, emptyPageContent); - const readmePath = path.join(projectPath, "README.md"); + const readmePath = path.join(projectPath, 'README.md'); writeFile(readmePath, `# ${projectName}`); - console.log(chalk.bold.green("Layout and pages created")); + console.log(chalk.bold.green('Layout and pages created')); - if (linter === "biome") { - console.log(chalk.blue("Setting up Biome linter...")); + if (linter === 'biome') { + console.log(chalk.blue('Setting up Biome linter...')); run(`${packageManager} install --save-dev @biomejs/biome`, projectPath); run(`npx @biomejs/biome init`, projectPath); - console.log(chalk.bold.green("Biome linter configured")); + console.log(chalk.bold.green('Biome linter configured')); } - if (orm === "prisma") { - console.log(chalk.blue("Setting up Prisma ORM...")); + if (orm === 'prisma') { + console.log(chalk.blue('Setting up Prisma ORM...')); run(`${packageManager} install --save-dev prisma`, projectPath); run(`${packageManager} install @prisma/client`, projectPath); run(`npx prisma init`, projectPath); - const prismaLibDir = useSrcDir ? path.join(projectPath, "src", "lib") : path.join(projectPath, "lib"); + const prismaLibDir = useSrcDir + ? path.join(projectPath, 'src', 'lib') + : path.join(projectPath, 'lib'); createFolder(prismaLibDir); const prismaContent = `import { PrismaClient } from '@prisma/client' @@ -260,13 +332,13 @@ import { createPages, createLayout } from './lib/templates.js'; if (process.env.NODE_ENV !== 'production') global.prisma = prisma export default prisma;`; - writeFile(path.join(prismaLibDir, "prisma.ts"), prismaContent); + writeFile(path.join(prismaLibDir, 'prisma.ts'), prismaContent); - console.log(chalk.bold.green("Prisma ORM configured")); + console.log(chalk.bold.green('Prisma ORM configured')); } - if (orm === "drizzle") { - console.log(chalk.blue("Setting up Drizzle ORM...")); + if (orm === 'drizzle') { + console.log(chalk.blue('Setting up Drizzle ORM...')); run(`${packageManager} install drizzle-orm @vercel/postgres`, projectPath); run(`${packageManager} install --save-dev drizzle-kit`, projectPath); @@ -281,9 +353,14 @@ import { createPages, createLayout } from './lib/templates.js'; connectionString: process.env.DATABASE_URL!, }, } satisfies Config;`; - writeFile(path.join(projectPath, "drizzle.config.ts"), drizzleConfigContent); + writeFile( + path.join(projectPath, 'drizzle.config.ts'), + drizzleConfigContent + ); - const dbDir = useSrcDir ? path.join(projectPath, "src", "db") : path.join(projectPath, "db"); + const dbDir = useSrcDir + ? path.join(projectPath, 'src', 'db') + : path.join(projectPath, 'db'); createFolder(dbDir); const schemaContent = `import { pgTable, serial, text } from 'drizzle-orm/pg-core'; @@ -292,52 +369,97 @@ import { createPages, createLayout } from './lib/templates.js'; id: serial('id').primaryKey(), name: text('name').notNull(), });`; - writeFile(path.join(dbDir, "schema.ts"), schemaContent); + writeFile(path.join(dbDir, 'schema.ts'), schemaContent); - console.log(chalk.bold.green("Drizzle ORM configured")); + console.log(chalk.bold.green('Drizzle ORM configured')); } if (useShadcn) { - console.log(chalk.magenta("Setting up Shadcn UI...")); + console.log(chalk.magenta('Setting up Shadcn UI...')); - run(`${packageManager} install --save-dev tailwindcss-animate class-variance-authority`, projectPath); + run( + `${packageManager} install --save-dev tailwindcss-animate class-variance-authority`, + projectPath + ); run(`npx shadcn@latest init`, projectPath); - const componentsJsonPath = path.join(projectPath, "components.json"); + const componentsJsonPath = path.join(projectPath, 'components.json'); const componentsJsonContent = { - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": useAppDir, - "tsx": useTypeScript, - "tailwind": { - "config": useTypeScript ? "tailwind.config.ts" : "tailwind.config.js", - "css": useAppDir - ? (useSrcDir ? "src/app/globals.css" : "app/globals.css") - : (useSrcDir ? "src/styles/globals.css" : "styles/globals.css"), - "baseColor": "slate", - "cssVariables": true + $schema: 'https://ui.shadcn.com/schema.json', + style: 'default', + rsc: useAppDir, + tsx: useTypeScript, + tailwind: { + config: useTypeScript ? 'tailwind.config.ts' : 'tailwind.config.js', + css: useAppDir + ? useSrcDir + ? 'src/app/globals.css' + : 'app/globals.css' + : useSrcDir + ? 'src/styles/globals.css' + : 'styles/globals.css', + baseColor: 'slate', + cssVariables: true, + }, + aliases: { + components: '@/components', + utils: '@/lib/utils', }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils" - } }; - writeFile(componentsJsonPath, JSON.stringify(componentsJsonContent, null, 2)); + writeFile( + componentsJsonPath, + JSON.stringify(componentsJsonContent, null, 2) + ); - console.log(chalk.bold.green("Shadcn UI configured")); + console.log(chalk.bold.green('Shadcn UI configured')); } - if (orm !== "none") { - const envContent = `DATABASE_URL="your_db_url"`; - writeFile(path.join(projectPath, ".env"), envContent); + if (auth !== 'none') { + console.log(chalk.blue(`Setting up ${auth} authentication...`)); + + if (auth === 'nextauth') { + run(`${packageManager} install next-auth`, projectPath); + } else if (auth === 'clerk') { + run(`${packageManager} install @clerk/nextjs`, projectPath); + } else if (auth === 'firebase') { + run(`${packageManager} install firebase`, projectPath); + } + + console.log(chalk.bold.green(`${auth} authentication configured`)); + } + + // .env file for ORM or Auth + if (orm !== 'none' || auth !== 'none') { + let envContent = ''; + + if (orm !== 'none') { + envContent += `DATABASE_URL="your_db_url"\n`; + } + + if (auth === 'nextauth') { + envContent += `NEXTAUTH_URL="http://localhost:3000"\nNEXTAUTH_SECRET="your_secret_here"\n`; + } else if (auth === 'clerk') { + envContent += `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="your_key_here"\nCLERK_SECRET_KEY="your_secret_here"\n`; + } else if (auth === 'firebase') { + envContent += `NEXT_PUBLIC_FIREBASE_API_KEY="your_api_key_here"\nNEXT_PUBLIC_FIREBASE_AUTH_DOMAIN="your_domain_here"\n`; + } + + if (envContent) { + console.log(chalk.yellow('Creating .env file...')); + writeFile(path.join(projectPath, '.env'), envContent); + } } console.log(); - console.log(chalk.bold.hex("#23f0bcff")("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")); - console.log(chalk.bold.white(" Setup complete!")); - console.log(chalk.bold.hex("#23f0bcff")("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")); + console.log( + chalk.bold.hex('#23f0bcff')('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━') + ); + console.log(chalk.bold.white(' Setup complete!')); + console.log( + chalk.bold.hex('#23f0bcff')('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━') + ); console.log(); - console.log(chalk.bold.white("-> Next steps:")); + console.log(chalk.bold.white('-> Next steps:')); console.log(chalk.cyan(` cd ${chalk.bold.white(projectName)}`)); console.log(chalk.cyan(` ${packageManager} ${chalk.bold.white(`run dev`)}`)); console.log(); diff --git a/test/generated-test-cases.js b/test/generated-test-cases.js index 5242967..e517509 100644 --- a/test/generated-test-cases.js +++ b/test/generated-test-cases.js @@ -7,258 +7,339 @@ import path from 'path'; export const testCases = [ { - "description": "should create a project with useTypeScript enabled", - "options": { - "packageManager": "", - "useTypeScript": true, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with useTypeScript enabled', + options: { + packageManager: '', + useTypeScript: true, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with useTypeScript disabled", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with useTypeScript disabled', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with useTailwind enabled", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": true, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with useTailwind enabled', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: true, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with useTailwind disabled", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with useTailwind disabled', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with useSrcDir enabled", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": true, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with useSrcDir enabled', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: true, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with useSrcDir disabled", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with useSrcDir disabled', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with useAppDir enabled", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": true, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with useAppDir enabled', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: true, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with useAppDir disabled", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with useAppDir disabled', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with custom pages", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "testpage", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with custom pages', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: 'testpage', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with linter set to none", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "none", - "orm": "", - "useShadcn": false + description: 'should create a project with linter set to none', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: 'none', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with linter set to eslint", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "eslint", - "orm": "", - "useShadcn": false + description: 'should create a project with linter set to eslint', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: 'eslint', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with linter set to biome", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "biome", - "orm": "", - "useShadcn": false + description: 'should create a project with linter set to biome', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: 'biome', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with orm set to none", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "none", - "useShadcn": false + description: 'should create a project with orm set to none', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: 'none', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with orm set to prisma", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "prisma", - "useShadcn": false + description: 'should create a project with orm set to prisma', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: 'prisma', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with orm set to drizzle", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "drizzle", - "useShadcn": false + description: 'should create a project with orm set to drizzle', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: 'drizzle', + useShadcn: false, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with useShadcn enabled", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": true + description: 'should create a project with useShadcn enabled', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: true, + auth: '', }, - "expectedFiles": [] + expectedFiles: [], }, { - "description": "should create a project with useShadcn disabled", - "options": { - "packageManager": "", - "useTypeScript": false, - "useTailwind": false, - "useSrcDir": false, - "useAppDir": false, - "pages": "", - "linter": "", - "orm": "", - "useShadcn": false + description: 'should create a project with useShadcn disabled', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: '', }, - "expectedFiles": [] - } + expectedFiles: [], + }, + { + description: 'should create a project with auth set to none', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: 'none', + }, + expectedFiles: [], + }, + { + description: 'should create a project with auth set to nextauth', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: 'nextauth', + }, + expectedFiles: [], + }, + { + description: 'should create a project with auth set to clerk', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: 'clerk', + }, + expectedFiles: [], + }, + { + description: 'should create a project with auth set to firebase', + options: { + packageManager: '', + useTypeScript: false, + useTailwind: false, + useSrcDir: false, + useAppDir: false, + pages: '', + linter: '', + orm: '', + useShadcn: false, + auth: 'firebase', + }, + expectedFiles: [], + }, ]; diff --git a/test/test-cases.js b/test/test-cases.js index 76519b0..f3451a0 100644 --- a/test/test-cases.js +++ b/test/test-cases.js @@ -1,4 +1,3 @@ - import { strict as assert } from 'assert'; import fs from 'fs'; import path from 'path'; @@ -15,9 +14,13 @@ export const testCases = [ linter: 'none', orm: 'none', useShadcn: false, + auth: 'none', }, assertions: (projectPath) => { - assert.ok(fs.existsSync(path.join(projectPath, 'package.json')), 'package.json should exist'); + assert.ok( + fs.existsSync(path.join(projectPath, 'package.json')), + 'package.json should exist' + ); }, }, { @@ -31,9 +34,13 @@ export const testCases = [ linter: 'none', orm: 'none', useShadcn: false, + auth: 'none', }, assertions: (projectPath) => { - assert.ok(fs.existsSync(path.join(projectPath, 'jsconfig.json')), 'jsconfig.json should exist'); + assert.ok( + fs.existsSync(path.join(projectPath, 'jsconfig.json')), + 'jsconfig.json should exist' + ); }, }, { @@ -47,9 +54,150 @@ export const testCases = [ linter: 'none', orm: 'none', useShadcn: false, + auth: 'none', }, assertions: (projectPath) => { - assert.ok(fs.existsSync(path.join(projectPath, 'src', 'app', 'about', 'page.tsx')), 'about page should exist'); + assert.ok( + fs.existsSync( + path.join(projectPath, 'src', 'app', 'about', 'page.tsx') + ), + 'about page should exist' + ); + }, + }, + { + description: 'should create a new project with NextAuth authentication', + options: { + useTypeScript: true, + useTailwind: true, + useSrcDir: true, + useAppDir: true, + pages: '', + linter: 'none', + orm: 'none', + useShadcn: false, + auth: 'nextauth', + }, + assertions: (projectPath) => { + assert.ok( + fs.existsSync(path.join(projectPath, 'package.json')), + 'package.json should exist' + ); + assert.ok( + fs.existsSync(path.join(projectPath, '.env')), + '.env file should exist' + ); + + const packageJson = JSON.parse( + fs.readFileSync(path.join(projectPath, 'package.json'), 'utf8') + ); + assert.ok( + packageJson.dependencies && packageJson.dependencies['next-auth'], + 'next-auth should be installed' + ); + + const envContent = fs.readFileSync( + path.join(projectPath, '.env'), + 'utf8' + ); + assert.ok( + envContent.includes('NEXTAUTH_URL'), + 'NEXTAUTH_URL should be in .env' + ); + assert.ok( + envContent.includes('NEXTAUTH_SECRET'), + 'NEXTAUTH_SECRET should be in .env' + ); + }, + }, + { + description: 'should create a new project with Clerk authentication', + options: { + useTypeScript: true, + useTailwind: true, + useSrcDir: true, + useAppDir: true, + pages: '', + linter: 'none', + orm: 'none', + useShadcn: false, + auth: 'clerk', + }, + assertions: (projectPath) => { + assert.ok( + fs.existsSync(path.join(projectPath, 'package.json')), + 'package.json should exist' + ); + assert.ok( + fs.existsSync(path.join(projectPath, '.env')), + '.env file should exist' + ); + + const packageJson = JSON.parse( + fs.readFileSync(path.join(projectPath, 'package.json'), 'utf8') + ); + assert.ok( + packageJson.dependencies && packageJson.dependencies['@clerk/nextjs'], + '@clerk/nextjs should be installed' + ); + + const envContent = fs.readFileSync( + path.join(projectPath, '.env'), + 'utf8' + ); + assert.ok( + envContent.includes('NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY'), + 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY should be in .env' + ); + assert.ok( + envContent.includes('CLERK_SECRET_KEY'), + 'CLERK_SECRET_KEY should be in .env' + ); + }, + }, + { + description: 'should create a new project with Firebase authentication', + options: { + useTypeScript: true, + useTailwind: true, + useSrcDir: true, + useAppDir: true, + pages: '', + linter: 'none', + orm: 'none', + useShadcn: false, + auth: 'firebase', + }, + assertions: (projectPath) => { + assert.ok( + fs.existsSync(path.join(projectPath, 'package.json')), + 'package.json should exist' + ); + assert.ok( + fs.existsSync(path.join(projectPath, '.env')), + '.env file should exist' + ); + + const packageJson = JSON.parse( + fs.readFileSync(path.join(projectPath, 'package.json'), 'utf8') + ); + assert.ok( + packageJson.dependencies && packageJson.dependencies['firebase'], + 'firebase should be installed' + ); + + const envContent = fs.readFileSync( + path.join(projectPath, '.env'), + 'utf8' + ); + assert.ok( + envContent.includes('NEXT_PUBLIC_FIREBASE_API_KEY'), + 'NEXT_PUBLIC_FIREBASE_API_KEY should be in .env' + ); + assert.ok( + envContent.includes('NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN'), + 'NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN should be in .env' + ); }, }, ]; diff --git a/test/test.js b/test/test.js index dadd3ef..6063f0b 100644 --- a/test/test.js +++ b/test/test.js @@ -62,6 +62,14 @@ describe('create-next-quick', function () { testCase.options.linter + '\n', testCase.options.orm + '\n', testCase.options.useShadcn ? 'y\n' : '\n', + (() => { + const auth = testCase.options.auth || 'none'; + if (!auth || auth === 'none') return '\n'; + const map = { nextauth: 1, clerk: 2, firebase: 3 }; + const downs = map[auth]; + if (!downs) return '\n'; + return '\x1B[B'.repeat(downs) + '\n'; + })(), ]; const assertions = () => {