diff --git a/src/env/envManager.ts b/src/env/envManager.ts index 7167a0a..7131c25 100644 --- a/src/env/envManager.ts +++ b/src/env/envManager.ts @@ -4,9 +4,9 @@ import logger from '../utils/logger.js' import { type StepResult } from '../types/index.js' export class EnvironmentManager { - async setupEnvironment(projectName: string, apiKey: string): Promise { + async setupEnvironment(projectName: string, apiKey: string, options: { isCurrentDir?: boolean } = { isCurrentDir: false }): Promise { try { - const projectPath = path.join(process.cwd(), projectName) + const projectPath = options.isCurrentDir ? process.cwd() : path.join(process.cwd(), projectName) // Ensure we're in the project directory try { diff --git a/src/generators/project.ts b/src/generators/project.ts index 73500d1..1121718 100644 --- a/src/generators/project.ts +++ b/src/generators/project.ts @@ -1,5 +1,5 @@ import execa from 'execa' -import fs, { createReadStream, createWriteStream } from 'fs' +import fs, { createReadStream, createWriteStream, Dirent } from 'fs' import path from 'path' import logger from '../utils/logger.js' @@ -10,14 +10,34 @@ import telemetry from '../utils/telemetry.js' export class ProjectGenerator { async createProject(options: ProjectGenerationOptions): Promise { try { - const projectPath = path.join(options.directory, options.name) + const projectPath = options.isCurrentDir ? process.cwd() : path.join(options.directory, options.name) // Check if directory already exists - try { - await fs.promises.access(projectPath, fs.constants.F_OK) - throw new Error(`Directory "${options.name}" already exists`) - } catch (error) { - // Directory doesn't exist, which is what we want + if (!options.isCurrentDir) { + try { + await fs.promises.access(projectPath, fs.constants.F_OK) + throw new Error(`Directory "${options.name}" already exists`) + } catch (error) { + // Directory doesn't exist, which is what we want + } + } else { + const files = await fs.promises.readdir(projectPath, { withFileTypes: true }); + const ignoredFiles = [ + ".git", + ".DS_Store", + "node_modules", + ".env", + "Thumbs.db", + ]; + const significantFiles = files.filter( + (file: Dirent) => file.isFile() && !ignoredFiles.includes(file.name) + ).map(file => file.name); + + if (significantFiles.length > 0) { + throw new Error( + "Current directory is not empty. Please use an empty directory or specify a different project name." + ); + } } logger.debug(`Creating project from template at: ${projectPath}`) diff --git a/src/index.ts b/src/index.ts index da5294b..6f707a5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -70,6 +70,14 @@ function checkNodeVersion(): void { } } +// Helper function to resolve project name from "." to currect directory name +function resolveProjectName(projectName: string): { name: string, isCurrentDir: boolean } { + if (projectName === '.') { + return { name: path.basename(process.cwd()), isCurrentDir: true } + } + return { name: projectName, isCurrentDir: false } +} + const TOTAL_STEPS = 3 class CreateC1App { private readonly spinner: SpinnerManager @@ -79,7 +87,8 @@ class CreateC1App { this.spinner = new SpinnerManager() this.config = { projectName: '', - template: 'app' + template: 'app', + isCurrentDir: false } } @@ -367,21 +376,35 @@ class CreateC1App { let projectName = options.projectName let template = options.template + let isCurrentDir = false // Project name - projectName ??= await input({ - message: 'What is your project name?', - default: 'my-c1-app', - prefill: 'editable', - validate: (input: string) => { - const validation = Validator.validateProjectName(input) - if (!validation.isValid) { - return validation.errors[0] - } - return true - }, - transformer: (input: string) => Validator.sanitizeProjectName(input) - }) + if (projectName === '' || projectName === undefined) { + projectName = await input({ + message: 'What is your project name?', + default: 'my-c1-app', + prefill: 'editable', + validate: (input: string) => { + const validation = Validator.validateProjectName(input) + if (!validation.isValid) { + return validation.errors[0] + } + return true + }, + transformer: (input: string) => Validator.sanitizeProjectName(input) + }) + } else { + const resolved = resolveProjectName(projectName) + projectName = resolved.name + isCurrentDir = resolved.isCurrentDir + + const validation = Validator.validateProjectName(projectName) + if (!validation.isValid) { + logger.error(`Invalid project name "${projectName}": ${validation.errors[0]}`) + logger.info('Please enter a valid project name') + process.exit(1) + } + } // Template selection if (template === undefined) { @@ -402,7 +425,8 @@ class CreateC1App { // Update config with answers and CLI options this.config = { projectName, - template: template || 'template-c1-component-next' + template: template || 'template-c1-component-next', + isCurrentDir } // Track project configuration @@ -426,7 +450,8 @@ class CreateC1App { const result = await generator.createProject({ name: this.config.projectName, template: this.config.template, - directory: process.cwd() + directory: process.cwd(), + isCurrentDir: this.config.isCurrentDir }) if (result.success) { @@ -460,7 +485,7 @@ class CreateC1App { try { const envManager = new EnvironmentManager() - const result = await envManager.setupEnvironment(this.config.projectName, apiKey) + const result = await envManager.setupEnvironment(this.config.projectName, apiKey, { isCurrentDir: this.config.isCurrentDir }) if (result.success) { // Track successful environment setup @@ -488,8 +513,11 @@ class CreateC1App { logger.newLine() logger.info('Your project is ready! Next steps:') - logger.info(` 1. cd ${this.config.projectName}`) - logger.info(' 2. npm run dev') + if (this.config.isCurrentDir) logger.info(' 1. npm run dev') + else { + logger.info(` 1. cd ${this.config.projectName}`) + logger.info(' 2. npm run dev') + } logger.newLine() logger.info('Happy coding! 🚀') @@ -532,6 +560,7 @@ export { CreateC1App } // Execute main function when run directly // ESM equivalent of require.main === module import { fileURLToPath } from 'url' +import path from 'path' const isMainModule = process.argv[1] === fileURLToPath(import.meta.url) if (isMainModule) { diff --git a/src/types/index.ts b/src/types/index.ts index 9125cc8..1d0f5db 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,7 @@ export interface CreateC1AppConfig { projectName: string template: string + isCurrentDir: boolean } export interface AuthCredentials { @@ -49,6 +50,7 @@ export interface ProjectGenerationOptions { name: string template: string directory: string + isCurrentDir: boolean } export interface EnvironmentConfig {