UNPKG

miridev-cli

Version:

Official CLI tool for deploying static sites to miri.dev - Deploy your websites in seconds

262 lines (218 loc) 6.59 kB
const chalk = require('chalk'); const path = require('path'); const fs = require('fs-extra'); const readline = require('readline'); const { createConfig } = require('../utils/files'); /** * Check if we're in an interactive environment */ function isInteractive() { return process.stdin.isTTY && process.stdout.isTTY && !process.env.CI; } /** * Get user input safely */ function getUserInput(question, defaultValue = '') { return new Promise((resolve) => { if (!isInteractive()) { // Non-interactive environment, use default values resolve(defaultValue); return; } const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const promptText = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `; rl.question(promptText, (answer) => { rl.close(); resolve(answer.trim() || defaultValue); }); }); } /** * Get confirmation safely */ function getConfirmation(question, defaultValue = false) { return new Promise((resolve) => { if (!isInteractive()) { // Non-interactive environment, use default resolve(defaultValue); return; } const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const defaultText = defaultValue ? 'Y/n' : 'y/N'; rl.question(`${question} (${defaultText}): `, (answer) => { rl.close(); const response = answer.trim().toLowerCase(); if (!response) { resolve(defaultValue); } else { resolve(response === 'y' || response === 'yes'); } }); }); } /** * 초기 설정 명령어 */ async function init(options) { console.log(chalk.blue.bold('\n🛠️ Initializing miri.dev configuration\n')); try { const configPath = path.resolve('miri.config.js'); // 기존 설정 파일 확인 if (await fs.pathExists(configPath) && !options.force) { const overwrite = await getConfirmation('Configuration file already exists. Overwrite?', false); if (!overwrite) { console.log(chalk.gray('Configuration initialization cancelled')); return; } } // 프로젝트 정보 수집 console.log(chalk.cyan('📋 Let\'s set up your project configuration:\n')); const siteName = await getUserInput('Site name (optional)', path.basename(process.cwd())); const customDomain = await getUserInput('Custom domain (optional)', ''); const needsBuild = await getConfirmation('Does your project need a build step?', false); let buildConfig = {}; if (needsBuild) { const buildCommand = await getUserInput('Build command', 'npm run build'); const buildDirectory = await getUserInput('Build output directory', detectBuildDirectory()); buildConfig = { command: buildCommand, directory: buildDirectory }; } // 고급 설정 const advancedConfig = await getConfirmation('Configure advanced deployment options?', false); let deployConfig = {}; if (advancedConfig) { const includeHidden = await getConfirmation('Include hidden files (starting with .)?', false); const maxFileSize = await getUserInput('Maximum file size limit (10MB/25MB/50MB/100MB)', '25MB'); deployConfig = { ignore: ['*.map', 'src/**', 'tests/**', 'docs/**', '*.md'], // Default ignore patterns includeHidden, maxFileSize }; } // 설정 파일 생성 const config = { site: { name: siteName || null, customDomain: customDomain || null }, build: needsBuild ? buildConfig : { command: null, directory: '.' }, deploy: { ignore: deployConfig.ignore || [], includeHidden: deployConfig.includeHidden || false, maxFileSize: deployConfig.maxFileSize || '25MB' } }; await createConfig(configPath, config); // .miriignore 파일 생성 제안 const ignoreFile = path.resolve('.miriignore'); if (!(await fs.pathExists(ignoreFile))) { const createIgnore = await getConfirmation('Create .miriignore file with common patterns?', true); if (createIgnore) { await createIgnoreFile(ignoreFile); } } console.log(chalk.green.bold('\n✨ Configuration setup complete!\n')); console.log(chalk.gray('Next steps:')); console.log(chalk.gray(' 1. Review and edit miri.config.js if needed')); console.log(chalk.gray(' 2. Run "miridev deploy" to deploy your site')); console.log(chalk.gray(' 3. Optional: Run "miridev login" for extended hosting')); } catch (error) { console.error(chalk.red.bold('\n✗ Initialization failed:')); console.error(chalk.red(error.message)); process.exit(1); } } /** * 빌드 디렉토리 자동 감지 */ function detectBuildDirectory() { const commonBuildDirs = ['dist', 'build', 'public', 'out', '_site']; for (const dir of commonBuildDirs) { if (fs.existsSync(dir)) { return dir; } } // package.json에서 빌드 스크립트 확인 try { const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); const buildScript = packageJson.scripts?.build; if (buildScript) { // Next.js if (buildScript.includes('next build')) { return 'out'; } // Nuxt.js if (buildScript.includes('nuxt generate')) { return 'dist'; } // Vue CLI if (buildScript.includes('vue-cli-service build')) { return 'dist'; } // Create React App if (buildScript.includes('react-scripts build')) { return 'build'; } // Vite if (buildScript.includes('vite build')) { return 'dist'; } } } catch (error) { // package.json이 없거나 파싱 실패 } return 'dist'; } /** * .miriignore 파일 생성 */ async function createIgnoreFile(filePath) { const ignoreContent = `# miri.dev deployment ignore file # Add patterns for files/directories to exclude from deployment # Development files src/ tests/ test/ __tests__/ *.test.js *.spec.js # Documentation README.md CHANGELOG.md LICENSE docs/ # Configuration files *.config.js .eslintrc* .prettierrc* tsconfig.json jsconfig.json # Source maps *.map # Logs *.log npm-debug.log* yarn-debug.log* # Editor directories and files .vscode/ .idea/ *.swp *.swo # OS generated files .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db `; await fs.writeFile(filePath, ignoreContent, 'utf8'); console.log(chalk.green('✓ Created .miriignore file')); } module.exports = init;