UNPKG

leetkick

Version:

A CLI tool for scaffolding LeetCode exercises with language-specific testing setups

126 lines (110 loc) 4.68 kB
import {Command} from 'commander'; import {fetchProblem} from '../utils/leetcode-api.js'; import {getAvailableLanguages, initializeLanguage} from '../utils/templates.js'; import {createProblemFiles} from '../utils/file-operations.js'; import {findWorkspaceRoot} from '../utils/workspace.js'; import {existsSync} from 'fs'; import {join} from 'path'; export const fetchCommand = new Command('fetch') .description('Fetch a LeetCode problem and create exercise files') .argument('<problem-slug>', 'LeetCode problem slug (e.g., "two-sum")') .option('-l, --language <language>', 'Programming language') .option('-f, --force', 'Overwrite existing exercise if it exists') .action( async ( problemSlug: string, options: {language?: string; force?: boolean}, ) => { try { const workspaceRoot = findWorkspaceRoot(); if (!workspaceRoot) { console.log( 'No leetkick workspace found. Run "leetkick init" first.', ); console.log( 'Make sure you are in a directory that contains .leetkick.json or run the command from within a leetkick workspace.', ); return; } console.log(`Fetching problem: ${problemSlug}...`); const problem = await fetchProblem(problemSlug); console.log( `✓ Found: [${problem.questionFrontendId}] ${problem.title}`, ); const availableLanguages = await getAvailableLanguages(); if (!options.language) { console.log('Available languages:', availableLanguages.join(', ')); throw new Error('Please specify a language with --language <lang>'); } if (!availableLanguages.includes(options.language)) { console.log('Available languages:', availableLanguages.join(', ')); throw new Error(`Language '${options.language}' not supported.`); } // Check if language workspace exists, initialize if not const languageDir = join(workspaceRoot, options.language); if (!existsSync(languageDir)) { console.log(`Initializing ${options.language} workspace...`); // Change to workspace root to create language directory there const originalCwd = process.cwd(); process.chdir(workspaceRoot); try { await initializeLanguage(options.language); } finally { process.chdir(originalCwd); } } // Check if exercise already exists const paddedId = problem.questionFrontendId.padStart(4, '0'); let problemDir: string; let problemName: string; let existingPath: string; if (options.language === 'kotlin' || options.language === 'java') { problemName = `problem${paddedId}`; problemDir = join( languageDir, 'src', 'main', options.language, problemName, ); existingPath = problemDir; } else if (options.language === 'rust') { problemName = `problem_${paddedId}`; problemDir = join(languageDir, 'src'); existingPath = join(problemDir, `${problemName}.rs`); } else { problemName = `problem_${paddedId}`; problemDir = join(languageDir, problemName); existingPath = problemDir; } if (existsSync(existingPath)) { if (!options.force) { console.log(`❌ Exercise already exists: ${existingPath}`); console.log('The exercise file/directory already exists.'); console.log('Options:'); console.log(' • Use --force to overwrite existing files'); console.log(' • Choose a different language'); console.log(' • Remove the existing file/directory manually'); return; // Exit gracefully without throwing error } else { console.log(`⚠️ Overwriting existing exercise: ${problemName}`); } } // Create problem files (change to workspace root since createProblemFiles uses process.cwd()) const originalCwd = process.cwd(); process.chdir(workspaceRoot); try { await createProblemFiles(problem, options.language); console.log( `✓ Created ${options.language} exercise for: ${problem.title}`, ); console.log(`📁 Problem ID: ${problemName}`); } finally { process.chdir(originalCwd); } } catch (error) { console.error('Error:', error instanceof Error ? error.message : error); throw error; } }, );