UNPKG

willow-cli

Version:

CLI for installing Willow Design System components

194 lines (193 loc) • 7.21 kB
import { fileExists, readFileContent } from './fileSystem.js'; import chalk from 'chalk'; /** * Enhanced project type detection with better environment awareness */ export async function detectProjectType() { console.log(chalk.blue('šŸ” Detecting project environment...')); try { const packageJsonContent = await readFileContent('package.json'); const pkg = JSON.parse(packageJsonContent); // Check for framework dependencies const isVite = !!(pkg.devDependencies?.vite || pkg.dependencies?.vite); const isNext = !!(pkg.devDependencies?.next || pkg.dependencies?.next); const isNuxt = !!(pkg.devDependencies?.nuxt || pkg.dependencies?.nuxt); const isRemix = !!(pkg.devDependencies?.['@remix-run/dev'] || pkg.dependencies?.['@remix-run/dev']); // Detect online IDE environments with more robust checks const isStackBlitz = process.env.SHELL?.includes('jsh') || !!process.env.STACKBLITZ; const isCodeSandbox = !!process.env.CODESANDBOX_SSE || !!process.env.CODESANDBOX; const isGitpod = !!process.env.GITPOD_WORKSPACE_ID; const isReplit = !!process.env.REPLIT_DB_URL || !!process.env.REPL_ID; const isBolt = await fileExists('.bolt'); const isGlitch = !!process.env.PROJECT_REMIX_CHAIN; const isOnlineIDE = isStackBlitz || isCodeSandbox || isGitpod || isBolt || isReplit || isGlitch; // Determine project type with priority let type = 'react'; if (isNext) type = 'nextjs'; else if (isVite) type = 'vite'; else if (isNuxt) type = 'nuxt'; else if (isRemix) type = 'remix'; const result = { isVite, isNext, isOnlineIDE, type, // Additional metadata hasTypeScript: !!(pkg.devDependencies?.typescript || pkg.dependencies?.typescript), packageManager: await detectPackageManager(), framework: type, }; console.log(chalk.green(`āœ… Project detected: ${type}${isOnlineIDE ? ' (Online IDE)' : ''}`)); console.log(chalk.gray(` TypeScript: ${result.hasTypeScript ? 'Yes' : 'No'}`)); console.log(chalk.gray(` Package Manager: ${result.packageManager}`)); return result; } catch (error) { console.warn(chalk.yellow('āš ļø Failed to read package.json, using defaults')); return { isVite: false, isNext: true, isOnlineIDE: false, type: 'nextjs', hasTypeScript: true, packageManager: 'npm', framework: 'nextjs', }; } } /** * Detect the package manager being used */ export async function detectPackageManager() { if (await fileExists('bun.lockb')) return 'bun'; if (await fileExists('pnpm-lock.yaml')) return 'pnpm'; if (await fileExists('yarn.lock')) return 'yarn'; return 'npm'; } /** * Check if the project has specific configurations */ export async function hasConfiguration(configType) { const configFiles = { tailwind: ['tailwind.config.js', 'tailwind.config.ts', 'tailwind.config.mjs'], postcss: ['postcss.config.js', 'postcss.config.ts', 'postcss.config.mjs'], typescript: ['tsconfig.json'], eslint: ['.eslintrc.js', '.eslintrc.json', '.eslintrc.yaml', '.eslintrc.yml', 'eslint.config.js'], }; const files = configFiles[configType] || []; for (const file of files) { if (await fileExists(file)) { return true; } } return false; } /** * Get project-specific directory preferences */ export async function getProjectStructure(projectType) { const structure = { vite: { componentsDir: 'src/components/ui', libDir: 'src/lib', cssPath: 'src/index.css', publicDir: 'public', }, nextjs: { componentsDir: 'components/ui', libDir: 'lib', cssPath: 'app/globals.css', publicDir: 'public', }, nuxt: { componentsDir: 'components/ui', libDir: 'lib', cssPath: 'assets/css/main.css', publicDir: 'public', }, remix: { componentsDir: 'app/components/ui', libDir: 'app/lib', cssPath: 'app/styles/global.css', publicDir: 'public', }, react: { componentsDir: 'src/components/ui', libDir: 'src/lib', cssPath: 'src/index.css', publicDir: 'public', }, }; return structure[projectType.type] || structure.react; } /** * Validate project requirements for Willow installation */ export async function validateProjectRequirements() { const issues = []; const warnings = []; // Check for package.json if (!(await fileExists('package.json'))) { issues.push('No package.json found - not a valid Node.js project'); } // Check for React try { const pkg = JSON.parse(await readFileContent('package.json')); const hasReact = !!(pkg.dependencies?.react || pkg.devDependencies?.react); if (!hasReact) { issues.push('React not found in dependencies - Willow requires React'); } // Check React version const reactVersion = pkg.dependencies?.react || pkg.devDependencies?.react; if (reactVersion && !reactVersion.includes('18') && !reactVersion.includes('19')) { warnings.push('React version may be incompatible - Willow is tested with React 18+'); } } catch { warnings.push('Could not validate React dependencies'); } // Check for conflicting design systems try { const pkg = JSON.parse(await readFileContent('package.json')); const conflictingLibs = [ 'chakra-ui', 'antd', 'mantine', '@mui/material', 'react-bootstrap', ]; const foundConflicts = conflictingLibs.filter(lib => pkg.dependencies?.[lib] || pkg.devDependencies?.[lib]); if (foundConflicts.length > 0) { warnings.push(`Conflicting UI libraries found: ${foundConflicts.join(', ')}`); } } catch { // Ignore validation errors } return { valid: issues.length === 0, issues, warnings, }; } /** * Log project environment details for debugging */ export function logProjectEnvironment(projectType) { console.log(chalk.blue('\nšŸ“‹ Project Environment Summary:')); console.log(chalk.gray(` Type: ${projectType.type}`)); console.log(chalk.gray(` Framework: ${projectType.framework || 'Unknown'}`)); console.log(chalk.gray(` TypeScript: ${projectType.hasTypeScript ? 'Yes' : 'No'}`)); console.log(chalk.gray(` Package Manager: ${projectType.packageManager || 'npm'}`)); console.log(chalk.gray(` Online IDE: ${projectType.isOnlineIDE ? 'Yes' : 'No'}`)); if (projectType.isOnlineIDE) { console.log(chalk.yellow(' āš ļø Online IDE optimizations will be applied')); } }